-- cgit v1.2.3 From 567ca812736b3c05b80219bea85169c406d9a279 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Wed, 6 Dec 2017 15:18:59 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3049 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 2578f46d6953ceb7f1cc2fd4d959c8662e2936d1 --- CMakeLists.txt | 1 + biblio/how_to_cite_gudhi.bib | 9 +++++++++ src/CMakeLists.txt | 1 + src/Doxyfile | 3 ++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 10373f75..b28dcbf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ add_gudhi_module(Subsampling) add_gudhi_module(Tangential_complex) add_gudhi_module(Witness_complex) add_gudhi_module(Nerve_GIC) +add_gudhi_module(Kernels) message("++ GUDHI_MODULES list is:\"${GUDHI_MODULES}\"") diff --git a/biblio/how_to_cite_gudhi.bib b/biblio/how_to_cite_gudhi.bib index 59c05a5b..b8c29109 100644 --- a/biblio/how_to_cite_gudhi.bib +++ b/biblio/how_to_cite_gudhi.bib @@ -122,3 +122,12 @@ , url = "http://gudhi.gforge.inria.fr/python/latest/" , year = 2016 } + +@incollection{gudhi:Kernels +, author = "Mathieu Carri\`ere" +, title = "Kernels for PDs" +, publisher = "{GUDHI Editorial Board}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "http://gudhi.gforge.inria.fr/python/latest/" +, year = 2017 +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 94587044..0ae26081 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,7 @@ add_gudhi_module(Subsampling) add_gudhi_module(Tangential_complex) add_gudhi_module(Witness_complex) add_gudhi_module(Nerve_GIC) +add_gudhi_module(Kernels) message("++ GUDHI_MODULES list is:\"${GUDHI_MODULES}\"") diff --git a/src/Doxyfile b/src/Doxyfile index 429bf6a1..bda6f03d 100644 --- a/src/Doxyfile +++ b/src/Doxyfile @@ -854,7 +854,8 @@ IMAGE_PATH = doc/Skeleton_blocker/ \ doc/Tangential_complex/ \ doc/Bottleneck_distance/ \ doc/Nerve_GIC/ \ - doc/Persistence_representations/ + doc/Persistence_representations/ \ + doc/Kernels/ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program -- cgit v1.2.3 -- cgit v1.2.3 From fd79fc0f0a216e5b1dc8b2cb466d383eb32c1fd4 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Fri, 8 Dec 2017 09:22:24 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3056 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 6fd757a9dfd0abe7ddfbaeca1af667a4c93af34b --- src/Kernels/include/gudhi/PSS.h | 108 +++++++++++++++ src/Kernels/include/gudhi/PWG.h | 204 ++++++++++++++++++++++++++++ src/Kernels/include/gudhi/SW.h | 288 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 600 insertions(+) create mode 100644 src/Kernels/include/gudhi/PSS.h create mode 100644 src/Kernels/include/gudhi/PWG.h create mode 100644 src/Kernels/include/gudhi/SW.h diff --git a/src/Kernels/include/gudhi/PSS.h b/src/Kernels/include/gudhi/PSS.h new file mode 100644 index 00000000..70743c47 --- /dev/null +++ b/src/Kernels/include/gudhi/PSS.h @@ -0,0 +1,108 @@ +/* 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): Mathieu Carrière + * + * Copyright (C) 2017 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 PSS_H_ +#define PSS_H_ + +#define NUMPI 3.14159265359 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../../figtree-0.9.3/include/figtree.h" +#include "../../figtree-0.9.3/external/ann_1.1.1/include/ANN/ANN.h" + +using PD = std::vector >; + +namespace Gudhi { +namespace persistence_scale_space { + +double compute_exact_pss(PD PD1, PD PD2, double sigma = 1){ + double k = 0; + for(int i = 0; i < PD1.size(); i++){ + for(int j = 0; j < PD2.size(); j++){ + k += exp( -( pow(PD1[i].first - PD2[j].first, 2) + pow(PD1[i].second - PD2[j].second, 2) )/(8*sigma)) -\ + exp( -( pow(PD1[i].first - PD2[j].second, 2) + pow(PD1[i].second - PD2[j].first, 2) )/(8*sigma)); + } + } + return k/(8*NUMPI*sigma); +} + +double compute_approximate_pss(PD PD1, PD PD2, double sigma = 1, double error = 1e-2){ + + double k = 0; + + int d = 2; int N = PD1.size(); int M = PD2.size(); double h = std::sqrt(8*sigma); + double* x = new double[2*N]; double* y = new double[2*M]; double* q = new double[N]; + for(int i = 0; i < N; i++){ + q[i] = 1.0/(8*NUMPI*sigma); + x[2*i] = PD1[i].first; x[2*i+1] = PD1[i].second; + } + for(int i = 0; i < M; i++){ y[2*i] = PD2[i].first; y[2*i+1] = PD2[i].second; } + double* g_auto = new double[M]; + memset(g_auto, 0, sizeof(double)*M); + + figtree(d, N, M, 1, x, h, q, y, error, g_auto); + for(int i = 0; i < M; i++) k += g_auto[i]; + + for(int i = 0; i < M; i++){ y[2*i] = PD2[i].second; y[2*i+1] = PD2[i].first; } + + figtree(d, N, M, 1, x, h, q, y, error, g_auto); + for(int i = 0; i < M; i++) k -= g_auto[i]; + + delete[] x; delete[] y; delete[] q; delete[] g_auto; + return k; +} + +} // namespace persistence_scale_space + +} // namespace Gudhi + +#endif // PSS_H_ diff --git a/src/Kernels/include/gudhi/PWG.h b/src/Kernels/include/gudhi/PWG.h new file mode 100644 index 00000000..bc491ae7 --- /dev/null +++ b/src/Kernels/include/gudhi/PWG.h @@ -0,0 +1,204 @@ +/* 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): Mathieu Carrière + * + * Copyright (C) 2017 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 PWG_H_ +#define PWG_H_ + +#define NUMPI 3.14159265359 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using PD = std::vector >; + +namespace Gudhi { +namespace persistence_weighted_gaussian { + +double compute_exact_linear_pwg(PD PD1, PD PD2, double sigma, double C, int p){ + + int num_pts1 = PD1.size(); + int num_pts2 = PD2.size(); + + double k = 0; + for(int i = 0; i < num_pts1; i++){ + for(int j = 0; j < num_pts2; j++){ + k += atan(C*pow(PD1[i].second-PD1[i].first,p))*atan(C*pow(PD2[j].second-PD2[j].first,p))*\ + exp( -( pow(PD1[i].first-PD2[j].first,2) + pow(PD1[i].second-PD2[j].second,2) )/(2*pow(sigma,2)) ); + } + } + + return k; + +} + +double compute_exact_gaussian_pwg(PD PD1, PD PD2, double sigma, double C, int p, double tau){ + + int num_pts1 = PD1.size(); + int num_pts2 = PD2.size(); + + double k1 = 0; + for(int i = 0; i < num_pts1; i++){ + for(int j = 0; j < num_pts1; j++){ + k1 += atan(C*pow(PD1[i].second-PD1[i].first,p))*atan(C*pow(PD1[j].second-PD1[j].first,p))*\ + exp( -( pow(PD1[i].first-PD1[j].first,2) + pow(PD1[i].second-PD1[j].second,2) )/(2*pow(sigma,2)) ); + } + } + + double k2 = 0; + for(int i = 0; i < num_pts2; i++){ + for(int j = 0; j < num_pts2; j++){ + k2 += atan(C*pow(PD2[i].second-PD2[i].first,p))*atan(C*pow(PD2[j].second-PD2[j].first,p))*\ + exp( -( pow(PD2[i].first-PD2[j].first,2) + pow(PD2[i].second-PD2[j].second,2) )/(2*pow(sigma,2)) ); + } + } + + double k3 = compute_exact_linear_pwg(PD1,PD2,sigma,C,p); + return exp( - (k1+k2-2*k3) / (2*pow(tau,2)) ); + +} + +double compute_exact_gaussian_RKHSdist(PD PD1, PD PD2, double sigma, double C, int p){ + + int num_pts1 = PD1.size(); + int num_pts2 = PD2.size(); + + double k1 = 0; + for(int i = 0; i < num_pts1; i++){ + for(int j = 0; j < num_pts1; j++){ + k1 += atan(C*pow(PD1[i].second-PD1[i].first,p))*atan(C*pow(PD1[j].second-PD1[j].first,p))*\ + exp( -( pow(PD1[i].first-PD1[j].first,2) + pow(PD1[i].second-PD1[j].second,2) )/(2*pow(sigma,2)) ); + } + } + + double k2 = 0; + for(int i = 0; i < num_pts2; i++){ + for(int j = 0; j < num_pts2; j++){ + k2 += atan(C*pow(PD2[i].second-PD2[i].first,p))*atan(C*pow(PD2[j].second-PD2[j].first,p))*\ + exp( -( pow(PD2[i].first-PD2[j].first,2) + pow(PD2[i].second-PD2[j].second,2) )/(2*pow(sigma,2)) ); + } + } + + double k3 = compute_exact_linear_pwg(PD1,PD2,sigma,C,p); + return std::sqrt(k1+k2-2*k3); + +} + +double compute_approximate_linear_pwg_from_Fourier_features(const std::vector >& B1, \ + const std::vector >& B2){ + double d = 0; int M = B1.size(); + for(int i = 0; i < M; i++) d += B1[i].first*B2[i].first + B1[i].second*B2[i].second; + return (1.0/M)*d; +} + +double compute_approximate_gaussian_pwg_from_Fourier_features(const std::vector >& B1, \ + const std::vector >& B2, double tau){ + int M = B1.size(); + double d3 = compute_approximate_linear_pwg_from_Fourier_features(B1, B2); + double d1 = 0; double d2 = 0; + for(int i = 0; i < M; i++){d1 += pow(B1[i].first,2) + pow(B1[i].second,2); d2 += pow(B2[i].first,2) + pow(B2[i].second,2);} + return exp( -((1.0/M)*(d1+d2)-2*d3) / (2*pow(tau,2)) ); +} + +double compute_approximate_gaussian_RKHSdist_from_Fourier_features(const std::vector >& B1, \ + const std::vector >& B2){ + int M = B1.size(); + double d3 = compute_approximate_linear_pwg_from_Fourier_features(B1, B2); + double d1 = 0; double d2 = 0; + for(int i = 0; i < M; i++){d1 += pow(B1[i].first,2) + pow(B1[i].second,2); d2 += pow(B2[i].first,2) + pow(B2[i].second,2);} + return std::sqrt((1.0/M)*(d1+d2)-2*d3); +} + +std::vector > compute_Fourier_features(double C, int p, PD D, std::vector > Z){ + int m = D.size(); std::vector > B; int M = Z.size(); + for(int i = 0; i < M; i++){ + double d1 = 0; double d2 = 0; double zx = Z[i].first; double zy = Z[i].second; + for(int j = 0; j < m; j++){ + double x = D[j].first; double y = D[j].second; + d1 += atan(C*pow(y-x,p))*cos(x*zx + y*zy); + d2 += atan(C*pow(y-x,p))*sin(x*zx + y*zy); + } + B.push_back(std::pair(d1,d2)); + } + return B; +} + +std::vector > random_Fourier(double sigma, int M = 1000){ + std::normal_distribution distrib(0,1); std::vector > Z; + std::random_device rd; + for(int i = 0; i < M; i++){ + //unsigned seedx = 2*i; unsigned seedy = 2*i+1; + //std::default_random_engine generatorx(seedx); std::default_random_engine generatory(seedy); + std::mt19937 e1(rd()); std::mt19937 e2(rd()); + double zx = distrib(e1/*generatorx*/); double zy = distrib(e2/*generatory*/); + Z.push_back(std::pair((1/sigma)*zx,(1/sigma)*zy)); + } + return Z; +} + +double compute_approximate_linear_pwg(PD PD1, PD PD2, double sigma, double C, int p, int M = 1000){ + std::vector > Z = random_Fourier(sigma, M); + std::vector > B1 = compute_Fourier_features(C,p,PD1,Z); + std::vector > B2 = compute_Fourier_features(C,p,PD2,Z); + return compute_approximate_linear_pwg_from_Fourier_features(B1,B2); +} + +double compute_approximate_gaussian_pwg(PD PD1, PD PD2, double sigma, double C, int p, double tau, int M = 1000){ + std::vector > Z = random_Fourier(sigma, M); + std::vector > B1 = compute_Fourier_features(C,p,PD1,Z); + std::vector > B2 = compute_Fourier_features(C,p,PD2,Z); + return compute_approximate_gaussian_pwg_from_Fourier_features(B1,B2,tau); +} + + +} // namespace persistence_weighted_gaussian + +} // namespace Gudhi + +#endif //PWG_H_ diff --git a/src/Kernels/include/gudhi/SW.h b/src/Kernels/include/gudhi/SW.h new file mode 100644 index 00000000..6871d990 --- /dev/null +++ b/src/Kernels/include/gudhi/SW.h @@ -0,0 +1,288 @@ +/* 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): Mathieu Carrière + * + * Copyright (C) 2017 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 SW_H_ +#define SW_H_ + +#define NUMPI 3.14159265359 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using PD = std::vector >; + +std::vector > PDi, PDj; + +bool compOri(const int& p, const int& q){ + if(PDi[p].second != PDi[q].second) + return (PDi[p].second < PDi[q].second); + else + return (PDi[p].first > PDi[q].first); +} + +bool compOrj(const int& p, const int& q){ + if(PDj[p].second != PDj[q].second) + return (PDj[p].second < PDj[q].second); + else + return (PDj[p].first > PDj[q].first); +} + +bool sortAngle(const std::pair >& p1, const std::pair >& p2){ + return p1.first < p2.first; +} + +bool myComp(const std::pair & P1, const std::pair & P2){return P1.second < P2.second;} + +namespace Gudhi { +namespace sliced_wasserstein { + + +double compute_approximate_SW(PD PD1, PD PD2, int N = 100){ + + double step = NUMPI/N; double sw = 0; + + // Add projections onto diagonal. + // ****************************** + int n1, n2; n1 = PD1.size(); n2 = PD2.size(); + for (int i = 0; i < n2; i++) + PD1.push_back(std::pair( (PD2[i].first+PD2[i].second)/2, (PD2[i].first+PD2[i].second)/2) ); + for (int i = 0; i < n1; i++) + PD2.push_back(std::pair( (PD1[i].first+PD1[i].second)/2, (PD1[i].first+PD1[i].second)/2) ); + int n = PD1.size(); + + // Sort and compare all projections. + // ********************************* + //#pragma omp parallel for + for (int i = 0; i < N; i++){ + std::vector > L1, L2; + for (int j = 0; j < n; j++){ + L1.push_back( std::pair(j, PD1[j].first*cos(-NUMPI/2+i*step) + PD1[j].second*sin(-NUMPI/2+i*step)) ); + L2.push_back( std::pair(j, PD2[j].first*cos(-NUMPI/2+i*step) + PD2[j].second*sin(-NUMPI/2+i*step)) ); + } + std::sort(L1.begin(),L1.end(), myComp); std::sort(L2.begin(),L2.end(), myComp); + double f = 0; for (int j = 0; j < n; j++) f += std::abs(L1[j].second - L2[j].second); + sw += f*step; + } + return sw/NUMPI; +} + +double compute_int_cos(const double& alpha, const double& beta){ // Valid only if alpha is in [-pi,pi] and beta-alpha is in [0,pi] + double res; + assert((alpha >= 0 && alpha <= NUMPI) || (alpha >= -NUMPI && alpha <= 0)); + if (alpha >= 0 && alpha <= NUMPI){ + if (cos(alpha) >= 0){ + if(NUMPI/2 <= beta){res = 2-sin(alpha)-sin(beta);} + else{res = sin(beta)-sin(alpha);} + } + else{ + if(1.5*NUMPI <= beta){res = 2+sin(alpha)+sin(beta);} + else{res = sin(alpha)-sin(beta);} + } + } + if (alpha >= -NUMPI && alpha <= 0){ + if (cos(alpha) <= 0){ + if(-NUMPI/2 <= beta){res = 2+sin(alpha)+sin(beta);} + else{res = sin(alpha)-sin(beta);} + } + else{ + if(NUMPI/2 <= beta){res = 2-sin(alpha)-sin(beta);} + else{res = sin(beta)-sin(alpha);} + } + } + return res; +} + +double compute_int(const double& theta1, const double& theta2, const int& p, const int& q){ + double norm = std::sqrt(pow(PDi[p].first-PDj[q].first,2) + pow(PDi[p].second-PDj[q].second,2)); + double angle1; + if (PDi[p].first > PDj[q].first) + angle1 = theta1 - asin( (PDi[p].second-PDj[q].second)/norm ); + else + angle1 = theta1 - asin( (PDj[q].second-PDi[p].second)/norm ); + double angle2 = angle1+theta2-theta1; + double integral = compute_int_cos(angle1,angle2); + return norm*integral; +} + +double compute_sw(const std::vector > >& V1, \ + const std::vector > >& V2){ + int N = V1.size(); double sw = 0; + for (int i = 0; i < N; i++){ + std::vector > U,V; U = V1[i]; V = V2[i]; + double theta1, theta2; theta1 = -NUMPI/2; + int ku, kv; ku = 0; kv = 0; theta2 = std::min(U[ku].second,V[kv].second); + while(theta1 != NUMPI/2){ + if(PDi[U[ku].first].first != PDj[V[kv].first].first || PDi[U[ku].first].second != PDj[V[kv].first].second) + if(theta1 != theta2) + sw += compute_int(theta1,theta2,U[ku].first,V[kv].first); + theta1 = theta2; + if ( (theta2 == U[ku].second) && ku < U.size()-1 ){ku++;} + if ( (theta2 == V[kv].second) && kv < V.size()-1 ){kv++;} + theta2 = std::min(U[ku].second, V[kv].second); + } + } + return sw/NUMPI; +} + +double compute_angle(const PD& PersDiag, const int& i, const int& j){ + std::pair vect; double x1,y1, x2,y2; + x1 = PersDiag[i].first; y1 = PersDiag[i].second; + x2 = PersDiag[j].first; y2 = PersDiag[j].second; + if (y1 - y2 > 0){ + vect.first = y1 - y2; + vect.second = x2 - x1;} + else{ + if(y1 - y2 < 0){ + vect.first = y2 - y1; + vect.second = x1 - x2; + } + else{ + vect.first = 0; + vect.second = abs(x1 - x2);} + } + double norm = std::sqrt(pow(vect.first,2) + pow(vect.second,2)); + return asin(vect.second/norm); +} + +double compute_exact_SW(PD PD1, PD PD2){ + + // Add projections onto diagonal. + // ****************************** + int n1, n2; n1 = PD1.size(); n2 = PD2.size(); double max_ordinate = std::numeric_limits::min(); + for (int i = 0; i < n2; i++){ + max_ordinate = std::max(max_ordinate, PD2[i].second); + PD1.push_back(std::pair( ((PD2[i].first+PD2[i].second)/2), ((PD2[i].first+PD2[i].second)/2)) ); + } + for (int i = 0; i < n1; i++){ + max_ordinate = std::max(max_ordinate, PD1[i].second); + PD2.push_back(std::pair( ((PD1[i].first+PD1[i].second)/2), ((PD1[i].first+PD1[i].second)/2)) ); + } + int N = PD1.size(); assert(N==PD2.size()); + + // Slightly perturb the points so that the PDs are in generic positions. + // ********************************************************************* + int mag = 0; while(max_ordinate > 10){mag++; max_ordinate/=10;} + double thresh = pow(10,-5+mag); + srand(time(NULL)); + for (int i = 0; i < N; i++){ + PD1[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); PD1[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); + PD2[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); PD2[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); + } + + // Compute all angles in both PDs. + // ******************************* + std::vector > > angles1, angles2; + for (int i = 0; i < N; i++){ + for (int j = i+1; j < N; j++){ + double theta1 = compute_angle(PD1,i,j); double theta2 = compute_angle(PD2,i,j); + angles1.push_back(std::pair >(theta1, std::pair(i,j))); + angles2.push_back(std::pair >(theta2, std::pair(i,j))); + } + } + + // Sort angles. + // ************ + std::sort(angles1.begin(), angles1.end(), sortAngle); std::sort(angles2.begin(), angles2.end(), sortAngle); + + // Initialize orders of the points of both PD (given by ordinates when theta = -pi/2). + // *********************************************************************************** + PDi = PD1; PDj = PD2; + std::vector orderp1, orderp2; + for (int i = 0; i < N; i++){orderp1.push_back(i); orderp2.push_back(i);} + std::sort(orderp1.begin(),orderp1.end(),compOri); std::sort(orderp2.begin(),orderp2.end(),compOrj); + + // Find the inverses of the orders. + // ******************************** + std::vector order1(N); std::vector order2(N); + for(int i = 0; i < N; i++){ + for (int j = 0; j < N; j++) + if(orderp1[j] == i) + order1[i] = j; + } + for(int i = 0; i < N; i++){ + for (int j = 0; j < N; j++) + if(orderp2[j] == i) + order2[i] = j; + } + + // Record all inversions of points in the orders as theta varies along the positive half-disk. + // ******************************************************************************************* + std::vector > > anglePerm1(N); + std::vector > > anglePerm2(N); + + int M1 = angles1.size(); + for (int i = 0; i < M1; i++){ + double theta = angles1[i].first; int p = angles1[i].second.first; int q = angles1[i].second.second; + anglePerm1[order1[p]].push_back(std::pair(p,theta)); + anglePerm1[order1[q]].push_back(std::pair(q,theta)); + int a = order1[p]; int b = order1[q]; order1[p] = b; order1[q] = a; + } + + int M2 = angles2.size(); + for (int i = 0; i < M2; i++){ + double theta = angles2[i].first; int p = angles2[i].second.first; int q = angles2[i].second.second; + anglePerm2[order2[p]].push_back(std::pair(p,theta)); + anglePerm2[order2[q]].push_back(std::pair(q,theta)); + int a = order2[p]; int b = order2[q]; order2[p] = b; order2[q] = a; + } + + for (int i = 0; i < N; i++){ + anglePerm1[order1[i]].push_back(std::pair(i,NUMPI/2)); + anglePerm2[order2[i]].push_back(std::pair(i,NUMPI/2)); + } + + // Compute the SW distance with the list of inversions. + // **************************************************** + return compute_sw(anglePerm1,anglePerm2); + +} + +}} + +#endif + + -- cgit v1.2.3 From 21e6cb713dd3db78e68b4140ab2d69508dad01af Mon Sep 17 00:00:00 2001 From: mcarrier Date: Fri, 22 Dec 2017 15:53:56 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3103 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: f7ac7ffce4fbfb04b099797a02999d4a93e3f22b --- src/Kernels/example/kernel_basic_example.cpp | 49 +++++++ src/Kernels/include/gudhi/PSS.h | 4 +- src/Kernels/include/gudhi/SW.h | 198 +++++++++++++++------------ src/Kernels/include/gudhi/figtree-0.9.3.zip | Bin 0 -> 1229617 bytes src/cmake/modules/GUDHI_modules.cmake | 4 +- 5 files changed, 166 insertions(+), 89 deletions(-) create mode 100644 src/Kernels/example/kernel_basic_example.cpp create mode 100644 src/Kernels/include/gudhi/figtree-0.9.3.zip diff --git a/src/Kernels/example/kernel_basic_example.cpp b/src/Kernels/example/kernel_basic_example.cpp new file mode 100644 index 00000000..0a8d83b3 --- /dev/null +++ b/src/Kernels/example/kernel_basic_example.cpp @@ -0,0 +1,49 @@ +/* This file is part of the Gudhi Library. The Gudhi library + * (Geometric Understanding in Higher Dimensions) is a generic C++ + * library for computational topology. + * + * Authors: Francois Godi, small modifications by Pawel Dlotko + * + * Copyright (C) 2015 INRIA + * + * 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 . + */ + +#include +//#include +//#include + +#include +#include +#include // for pair +#include // for numeric_limits + +int main() { + std::vector< std::pair > v1, v2; + + v1.emplace_back(2.7, 3.7); + v1.emplace_back(9.6, 14.); + v1.emplace_back(34.2, 34.974); + + v2.emplace_back(2.8, 4.45); + v2.emplace_back(9.5, 14.1); + + + double b1 = Gudhi::sliced_wasserstein::compute_approximate_SW (v1, v2); + double b2 = Gudhi::sliced_wasserstein::compute_exact_SW (v1, v2); + + std::cout << "Approximate Sliced Wasserstein distance = " << b1 << std::endl; + std::cout << "Exact Sliced Wasserstein distance = " << b2 << std::endl; + +} diff --git a/src/Kernels/include/gudhi/PSS.h b/src/Kernels/include/gudhi/PSS.h index 70743c47..5111a09f 100644 --- a/src/Kernels/include/gudhi/PSS.h +++ b/src/Kernels/include/gudhi/PSS.h @@ -56,8 +56,8 @@ #include #include -#include "../../figtree-0.9.3/include/figtree.h" -#include "../../figtree-0.9.3/external/ann_1.1.1/include/ANN/ANN.h" +#include "figtree.h" +#include "ANN.h" using PD = std::vector >; diff --git a/src/Kernels/include/gudhi/SW.h b/src/Kernels/include/gudhi/SW.h index 6871d990..0b041252 100644 --- a/src/Kernels/include/gudhi/SW.h +++ b/src/Kernels/include/gudhi/SW.h @@ -55,47 +55,36 @@ using PD = std::vector >; -std::vector > PDi, PDj; - -bool compOri(const int& p, const int& q){ - if(PDi[p].second != PDi[q].second) - return (PDi[p].second < PDi[q].second); - else - return (PDi[p].first > PDi[q].first); -} - -bool compOrj(const int& p, const int& q){ - if(PDj[p].second != PDj[q].second) - return (PDj[p].second < PDj[q].second); - else - return (PDj[p].first > PDj[q].first); -} - -bool sortAngle(const std::pair >& p1, const std::pair >& p2){ - return p1.first < p2.first; -} - +bool sortAngle(const std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);} bool myComp(const std::pair & P1, const std::pair & P2){return P1.second < P2.second;} namespace Gudhi { namespace sliced_wasserstein { +// ******************************************************************** +// Approximate computation. +// ******************************************************************** + +/** \brief Computes an approximation of the Sliced Wasserstein distance between two persistence diagrams + * + * @param[in] N number of points sampled on the circle. + * + */ + double compute_approximate_SW(PD PD1, PD PD2, int N = 100){ double step = NUMPI/N; double sw = 0; // Add projections onto diagonal. - // ****************************** int n1, n2; n1 = PD1.size(); n2 = PD2.size(); for (int i = 0; i < n2; i++) - PD1.push_back(std::pair( (PD2[i].first+PD2[i].second)/2, (PD2[i].first+PD2[i].second)/2) ); + PD1.push_back(std::pair( (PD2[i].first + PD2[i].second)/2, (PD2[i].first + PD2[i].second)/2) ); for (int i = 0; i < n1; i++) - PD2.push_back(std::pair( (PD1[i].first+PD1[i].second)/2, (PD1[i].first+PD1[i].second)/2) ); + PD2.push_back(std::pair( (PD1[i].first + PD1[i].second)/2, (PD1[i].first + PD1[i].second)/2) ); int n = PD1.size(); // Sort and compare all projections. - // ********************************* //#pragma omp parallel for for (int i = 0; i < N; i++){ std::vector > L1, L2; @@ -110,8 +99,55 @@ double compute_approximate_SW(PD PD1, PD PD2, int N = 100){ return sw/NUMPI; } -double compute_int_cos(const double& alpha, const double& beta){ // Valid only if alpha is in [-pi,pi] and beta-alpha is in [0,pi] - double res; + + + + + + + + + + + + + + + + + + +// ******************************************************************** +// Exact computation. +// ******************************************************************** + + + +// Compute the angle formed by two points of a PD +double compute_angle(const PD & PersDiag, const int & i, const int & j){ + std::pair vect; double x1,y1, x2,y2; + x1 = PersDiag[i].first; y1 = PersDiag[i].second; + x2 = PersDiag[j].first; y2 = PersDiag[j].second; + if (y1 - y2 > 0){ + vect.first = y1 - y2; + vect.second = x2 - x1;} + else{ + if(y1 - y2 < 0){ + vect.first = y2 - y1; + vect.second = x1 - x2; + } + else{ + vect.first = 0; + vect.second = abs(x1 - x2);} + } + double norm = std::sqrt(pow(vect.first,2) + pow(vect.second,2)); + return asin(vect.second/norm); +} + +// Compute the integral of |cos()| between alpha and beta +// Valid only if alpha is in [-pi,pi] and beta-alpha is in [0,pi] +double compute_int_cos(const double & alpha, const double & beta){ + double res = 0; assert((alpha >= 0 && alpha <= NUMPI) || (alpha >= -NUMPI && alpha <= 0)); if (alpha >= 0 && alpha <= NUMPI){ if (cos(alpha) >= 0){ @@ -136,75 +172,76 @@ double compute_int_cos(const double& alpha, const double& beta){ // Valid only i return res; } -double compute_int(const double& theta1, const double& theta2, const int& p, const int& q){ - double norm = std::sqrt(pow(PDi[p].first-PDj[q].first,2) + pow(PDi[p].second-PDj[q].second,2)); +double compute_int(const double & theta1, const double & theta2, const int & p, const int & q, const PD & PD1, const PD & PD2){ + double norm = std::sqrt(pow(PD1[p].first-PD2[q].first,2) + pow(PD1[p].second-PD2[q].second,2)); double angle1; - if (PDi[p].first > PDj[q].first) - angle1 = theta1 - asin( (PDi[p].second-PDj[q].second)/norm ); + if (PD1[p].first > PD2[q].first) + angle1 = theta1 - asin( (PD1[p].second-PD2[q].second)/norm ); else - angle1 = theta1 - asin( (PDj[q].second-PDi[p].second)/norm ); - double angle2 = angle1+theta2-theta1; + angle1 = theta1 - asin( (PD2[q].second-PD1[p].second)/norm ); + double angle2 = angle1 + theta2 - theta1; double integral = compute_int_cos(angle1,angle2); return norm*integral; } -double compute_sw(const std::vector > >& V1, \ - const std::vector > >& V2){ + + +double compute_sw(const std::vector > > & V1, const std::vector > > & V2, const PD & PD1, const PD & PD2){ int N = V1.size(); double sw = 0; for (int i = 0; i < N; i++){ std::vector > U,V; U = V1[i]; V = V2[i]; double theta1, theta2; theta1 = -NUMPI/2; - int ku, kv; ku = 0; kv = 0; theta2 = std::min(U[ku].second,V[kv].second); + unsigned int ku, kv; ku = 0; kv = 0; theta2 = std::min(U[ku].second,V[kv].second); while(theta1 != NUMPI/2){ - if(PDi[U[ku].first].first != PDj[V[kv].first].first || PDi[U[ku].first].second != PDj[V[kv].first].second) + if(PD1[U[ku].first].first != PD2[V[kv].first].first || PD1[U[ku].first].second != PD2[V[kv].first].second) if(theta1 != theta2) - sw += compute_int(theta1,theta2,U[ku].first,V[kv].first); + sw += Gudhi::sliced_wasserstein::compute_int(theta1, theta2, U[ku].first, V[kv].first, PD1, PD2); theta1 = theta2; - if ( (theta2 == U[ku].second) && ku < U.size()-1 ){ku++;} - if ( (theta2 == V[kv].second) && kv < V.size()-1 ){kv++;} + if ( (theta2 == U[ku].second) && ku < U.size()-1 ) ku++; + if ( (theta2 == V[kv].second) && kv < V.size()-1 ) kv++; theta2 = std::min(U[ku].second, V[kv].second); } } return sw/NUMPI; } -double compute_angle(const PD& PersDiag, const int& i, const int& j){ - std::pair vect; double x1,y1, x2,y2; - x1 = PersDiag[i].first; y1 = PersDiag[i].second; - x2 = PersDiag[j].first; y2 = PersDiag[j].second; - if (y1 - y2 > 0){ - vect.first = y1 - y2; - vect.second = x2 - x1;} - else{ - if(y1 - y2 < 0){ - vect.first = y2 - y1; - vect.second = x1 - x2; - } - else{ - vect.first = 0; - vect.second = abs(x1 - x2);} - } - double norm = std::sqrt(pow(vect.first,2) + pow(vect.second,2)); - return asin(vect.second/norm); -} + + + + + + + + + + + + + + + + + + +/** \brief Computes the Sliced Wasserstein distance between two persistence diagrams + * + */ double compute_exact_SW(PD PD1, PD PD2){ // Add projections onto diagonal. - // ****************************** - int n1, n2; n1 = PD1.size(); n2 = PD2.size(); double max_ordinate = std::numeric_limits::min(); + int n1, n2; n1 = PD1.size(); n2 = PD2.size(); double max_ordinate = std::numeric_limits::lowest(); for (int i = 0; i < n2; i++){ max_ordinate = std::max(max_ordinate, PD2[i].second); - PD1.push_back(std::pair( ((PD2[i].first+PD2[i].second)/2), ((PD2[i].first+PD2[i].second)/2)) ); + PD1.push_back( std::pair( ((PD2[i].first+PD2[i].second)/2), ((PD2[i].first+PD2[i].second)/2) ) ); } for (int i = 0; i < n1; i++){ max_ordinate = std::max(max_ordinate, PD1[i].second); - PD2.push_back(std::pair( ((PD1[i].first+PD1[i].second)/2), ((PD1[i].first+PD1[i].second)/2)) ); + PD2.push_back( std::pair( ((PD1[i].first+PD1[i].second)/2), ((PD1[i].first+PD1[i].second)/2) ) ); } int N = PD1.size(); assert(N==PD2.size()); // Slightly perturb the points so that the PDs are in generic positions. - // ********************************************************************* int mag = 0; while(max_ordinate > 10){mag++; max_ordinate/=10;} double thresh = pow(10,-5+mag); srand(time(NULL)); @@ -214,43 +251,30 @@ double compute_exact_SW(PD PD1, PD PD2){ } // Compute all angles in both PDs. - // ******************************* std::vector > > angles1, angles2; for (int i = 0; i < N; i++){ for (int j = i+1; j < N; j++){ - double theta1 = compute_angle(PD1,i,j); double theta2 = compute_angle(PD2,i,j); + double theta1 = Gudhi::sliced_wasserstein::compute_angle(PD1,i,j); double theta2 = Gudhi::sliced_wasserstein::compute_angle(PD2,i,j); angles1.push_back(std::pair >(theta1, std::pair(i,j))); angles2.push_back(std::pair >(theta2, std::pair(i,j))); } } // Sort angles. - // ************ std::sort(angles1.begin(), angles1.end(), sortAngle); std::sort(angles2.begin(), angles2.end(), sortAngle); - // Initialize orders of the points of both PD (given by ordinates when theta = -pi/2). - // *********************************************************************************** - PDi = PD1; PDj = PD2; + // Initialize orders of the points of both PDs (given by ordinates when theta = -pi/2). std::vector orderp1, orderp2; - for (int i = 0; i < N; i++){orderp1.push_back(i); orderp2.push_back(i);} - std::sort(orderp1.begin(),orderp1.end(),compOri); std::sort(orderp2.begin(),orderp2.end(),compOrj); + for (int i = 0; i < N; i++){ orderp1.push_back(i); orderp2.push_back(i); } + std::sort( orderp1.begin(), orderp1.end(), [=](int i, int j){ if(PD1[i].second != PD1[j].second) return (PD1[i].second < PD1[j].second); else return (PD1[i].first > PD1[j].first); } ); + std::sort( orderp2.begin(), orderp2.end(), [=](int i, int j){ if(PD2[i].second != PD2[j].second) return (PD2[i].second < PD2[j].second); else return (PD2[i].first > PD2[j].first); } ); // Find the inverses of the orders. - // ******************************** std::vector order1(N); std::vector order2(N); - for(int i = 0; i < N; i++){ - for (int j = 0; j < N; j++) - if(orderp1[j] == i) - order1[i] = j; - } - for(int i = 0; i < N; i++){ - for (int j = 0; j < N; j++) - if(orderp2[j] == i) - order2[i] = j; - } + for(int i = 0; i < N; i++) for (int j = 0; j < N; j++) if(orderp1[j] == i){ order1[i] = j; break; } + for(int i = 0; i < N; i++) for (int j = 0; j < N; j++) if(orderp2[j] == i){ order2[i] = j; break; } // Record all inversions of points in the orders as theta varies along the positive half-disk. - // ******************************************************************************************* std::vector > > anglePerm1(N); std::vector > > anglePerm2(N); @@ -276,11 +300,15 @@ double compute_exact_SW(PD PD1, PD PD2){ } // Compute the SW distance with the list of inversions. - // **************************************************** - return compute_sw(anglePerm1,anglePerm2); + return Gudhi::sliced_wasserstein::compute_sw(anglePerm1, anglePerm2, PD1, PD2); } + + + + + }} #endif diff --git a/src/Kernels/include/gudhi/figtree-0.9.3.zip b/src/Kernels/include/gudhi/figtree-0.9.3.zip new file mode 100644 index 00000000..a9468274 Binary files /dev/null and b/src/Kernels/include/gudhi/figtree-0.9.3.zip differ diff --git a/src/cmake/modules/GUDHI_modules.cmake b/src/cmake/modules/GUDHI_modules.cmake index f95d0c34..205ee8a1 100644 --- a/src/cmake/modules/GUDHI_modules.cmake +++ b/src/cmake/modules/GUDHI_modules.cmake @@ -16,8 +16,8 @@ function(add_gudhi_module file_path) endfunction(add_gudhi_module) -option(WITH_GUDHI_BENCHMARK "Activate/desactivate benchmark compilation" OFF) -option(WITH_GUDHI_EXAMPLE "Activate/desactivate examples compilation and installation" OFF) +option(WITH_GUDHI_BENCHMARK "Activate/desactivate benchmark compilation" ON) +option(WITH_GUDHI_EXAMPLE "Activate/desactivate examples compilation and installation" ON) option(WITH_GUDHI_PYTHON "Activate/desactivate python module compilation and installation" ON) option(WITH_GUDHI_TEST "Activate/desactivate examples compilation and installation" ON) option(WITH_GUDHI_UTILITIES "Activate/desactivate utilities compilation and installation" ON) -- cgit v1.2.3 From a065a3b86e33c24250a981f95db1ff46d9075ef5 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Fri, 29 Dec 2017 23:13:11 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3106 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: d4456c36f3ef8cc9e7f516d943472b963b5e9b93 --- src/Kernels/example/CMakeLists.txt | 11 + src/Kernels/example/kernel_basic_example.cpp | 31 +- src/Kernels/include/gudhi/PSS.h | 108 ------- src/Kernels/include/gudhi/PWG.h | 204 ------------ src/Kernels/include/gudhi/SW.h | 316 ------------------- src/Kernels/include/gudhi/figtree-0.9.3.zip | Bin 1229617 -> 0 bytes src/Kernels/include/gudhi/kernel.h | 447 +++++++++++++++++++++++++++ 7 files changed, 473 insertions(+), 644 deletions(-) create mode 100644 src/Kernels/example/CMakeLists.txt delete mode 100644 src/Kernels/include/gudhi/PSS.h delete mode 100644 src/Kernels/include/gudhi/PWG.h delete mode 100644 src/Kernels/include/gudhi/SW.h delete mode 100644 src/Kernels/include/gudhi/figtree-0.9.3.zip create mode 100644 src/Kernels/include/gudhi/kernel.h diff --git a/src/Kernels/example/CMakeLists.txt b/src/Kernels/example/CMakeLists.txt new file mode 100644 index 00000000..57e13004 --- /dev/null +++ b/src/Kernels/example/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.6) +project(Kernels_examples) + +add_executable ( BasicEx kernel_basic_example.cpp ) + +if (TBB_FOUND) + target_link_libraries(BasicEx ${TBB_LIBRARIES}) +endif() + +add_test(NAME Kernels_example_basicex COMMAND $ + "") diff --git a/src/Kernels/example/kernel_basic_example.cpp b/src/Kernels/example/kernel_basic_example.cpp index 0a8d83b3..8e9925c5 100644 --- a/src/Kernels/example/kernel_basic_example.cpp +++ b/src/Kernels/example/kernel_basic_example.cpp @@ -2,9 +2,9 @@ * (Geometric Understanding in Higher Dimensions) is a generic C++ * library for computational topology. * - * Authors: Francois Godi, small modifications by Pawel Dlotko + * Authors: Mathieu Carrière * - * Copyright (C) 2015 INRIA + * Copyright (C) 2017 INRIA * * 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 @@ -20,18 +20,15 @@ * along with this program. If not, see . */ -#include -//#include -//#include - -#include -#include -#include // for pair -#include // for numeric_limits +#define NUMPI 3.14159265359 +#include int main() { + std::vector< std::pair > v1, v2; + double sigma = 2; double tau = 5; + v1.emplace_back(2.7, 3.7); v1.emplace_back(9.6, 14.); v1.emplace_back(34.2, 34.974); @@ -39,11 +36,13 @@ int main() { v2.emplace_back(2.8, 4.45); v2.emplace_back(9.5, 14.1); - - double b1 = Gudhi::sliced_wasserstein::compute_approximate_SW (v1, v2); - double b2 = Gudhi::sliced_wasserstein::compute_exact_SW (v1, v2); - - std::cout << "Approximate Sliced Wasserstein distance = " << b1 << std::endl; - std::cout << "Exact Sliced Wasserstein distance = " << b2 << std::endl; + std::cout << "SW exact = " << Gudhi::kernel::sw (v1, v2) << std::endl; + std::cout << "SW approx = " << Gudhi::kernel::approx_sw (v1, v2) << std::endl; + std::cout << "PSS exact = " << Gudhi::kernel::pss (v1,v2,sigma) << std::endl; + std::cout << "PSS approx = " << Gudhi::kernel::approx_pss (v1,v2,sigma) << std::endl; + std::cout << "PWG exact = " << Gudhi::kernel::lpwg (v1,v2,sigma) << std::endl; + std::cout << "PWG approx = " << Gudhi::kernel::approx_lpwg (v1,v2,sigma) << std::endl; + std::cout << "GPWG exact = " << Gudhi::kernel::gpwg (v1,v2,sigma,tau) << std::endl; + std::cout << "GPWG approx = " << Gudhi::kernel::approx_gpwg (v1,v2,sigma,tau) << std::endl; } diff --git a/src/Kernels/include/gudhi/PSS.h b/src/Kernels/include/gudhi/PSS.h deleted file mode 100644 index 5111a09f..00000000 --- a/src/Kernels/include/gudhi/PSS.h +++ /dev/null @@ -1,108 +0,0 @@ -/* 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): Mathieu Carrière - * - * Copyright (C) 2017 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 PSS_H_ -#define PSS_H_ - -#define NUMPI 3.14159265359 - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "figtree.h" -#include "ANN.h" - -using PD = std::vector >; - -namespace Gudhi { -namespace persistence_scale_space { - -double compute_exact_pss(PD PD1, PD PD2, double sigma = 1){ - double k = 0; - for(int i = 0; i < PD1.size(); i++){ - for(int j = 0; j < PD2.size(); j++){ - k += exp( -( pow(PD1[i].first - PD2[j].first, 2) + pow(PD1[i].second - PD2[j].second, 2) )/(8*sigma)) -\ - exp( -( pow(PD1[i].first - PD2[j].second, 2) + pow(PD1[i].second - PD2[j].first, 2) )/(8*sigma)); - } - } - return k/(8*NUMPI*sigma); -} - -double compute_approximate_pss(PD PD1, PD PD2, double sigma = 1, double error = 1e-2){ - - double k = 0; - - int d = 2; int N = PD1.size(); int M = PD2.size(); double h = std::sqrt(8*sigma); - double* x = new double[2*N]; double* y = new double[2*M]; double* q = new double[N]; - for(int i = 0; i < N; i++){ - q[i] = 1.0/(8*NUMPI*sigma); - x[2*i] = PD1[i].first; x[2*i+1] = PD1[i].second; - } - for(int i = 0; i < M; i++){ y[2*i] = PD2[i].first; y[2*i+1] = PD2[i].second; } - double* g_auto = new double[M]; - memset(g_auto, 0, sizeof(double)*M); - - figtree(d, N, M, 1, x, h, q, y, error, g_auto); - for(int i = 0; i < M; i++) k += g_auto[i]; - - for(int i = 0; i < M; i++){ y[2*i] = PD2[i].second; y[2*i+1] = PD2[i].first; } - - figtree(d, N, M, 1, x, h, q, y, error, g_auto); - for(int i = 0; i < M; i++) k -= g_auto[i]; - - delete[] x; delete[] y; delete[] q; delete[] g_auto; - return k; -} - -} // namespace persistence_scale_space - -} // namespace Gudhi - -#endif // PSS_H_ diff --git a/src/Kernels/include/gudhi/PWG.h b/src/Kernels/include/gudhi/PWG.h deleted file mode 100644 index bc491ae7..00000000 --- a/src/Kernels/include/gudhi/PWG.h +++ /dev/null @@ -1,204 +0,0 @@ -/* 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): Mathieu Carrière - * - * Copyright (C) 2017 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 PWG_H_ -#define PWG_H_ - -#define NUMPI 3.14159265359 - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -using PD = std::vector >; - -namespace Gudhi { -namespace persistence_weighted_gaussian { - -double compute_exact_linear_pwg(PD PD1, PD PD2, double sigma, double C, int p){ - - int num_pts1 = PD1.size(); - int num_pts2 = PD2.size(); - - double k = 0; - for(int i = 0; i < num_pts1; i++){ - for(int j = 0; j < num_pts2; j++){ - k += atan(C*pow(PD1[i].second-PD1[i].first,p))*atan(C*pow(PD2[j].second-PD2[j].first,p))*\ - exp( -( pow(PD1[i].first-PD2[j].first,2) + pow(PD1[i].second-PD2[j].second,2) )/(2*pow(sigma,2)) ); - } - } - - return k; - -} - -double compute_exact_gaussian_pwg(PD PD1, PD PD2, double sigma, double C, int p, double tau){ - - int num_pts1 = PD1.size(); - int num_pts2 = PD2.size(); - - double k1 = 0; - for(int i = 0; i < num_pts1; i++){ - for(int j = 0; j < num_pts1; j++){ - k1 += atan(C*pow(PD1[i].second-PD1[i].first,p))*atan(C*pow(PD1[j].second-PD1[j].first,p))*\ - exp( -( pow(PD1[i].first-PD1[j].first,2) + pow(PD1[i].second-PD1[j].second,2) )/(2*pow(sigma,2)) ); - } - } - - double k2 = 0; - for(int i = 0; i < num_pts2; i++){ - for(int j = 0; j < num_pts2; j++){ - k2 += atan(C*pow(PD2[i].second-PD2[i].first,p))*atan(C*pow(PD2[j].second-PD2[j].first,p))*\ - exp( -( pow(PD2[i].first-PD2[j].first,2) + pow(PD2[i].second-PD2[j].second,2) )/(2*pow(sigma,2)) ); - } - } - - double k3 = compute_exact_linear_pwg(PD1,PD2,sigma,C,p); - return exp( - (k1+k2-2*k3) / (2*pow(tau,2)) ); - -} - -double compute_exact_gaussian_RKHSdist(PD PD1, PD PD2, double sigma, double C, int p){ - - int num_pts1 = PD1.size(); - int num_pts2 = PD2.size(); - - double k1 = 0; - for(int i = 0; i < num_pts1; i++){ - for(int j = 0; j < num_pts1; j++){ - k1 += atan(C*pow(PD1[i].second-PD1[i].first,p))*atan(C*pow(PD1[j].second-PD1[j].first,p))*\ - exp( -( pow(PD1[i].first-PD1[j].first,2) + pow(PD1[i].second-PD1[j].second,2) )/(2*pow(sigma,2)) ); - } - } - - double k2 = 0; - for(int i = 0; i < num_pts2; i++){ - for(int j = 0; j < num_pts2; j++){ - k2 += atan(C*pow(PD2[i].second-PD2[i].first,p))*atan(C*pow(PD2[j].second-PD2[j].first,p))*\ - exp( -( pow(PD2[i].first-PD2[j].first,2) + pow(PD2[i].second-PD2[j].second,2) )/(2*pow(sigma,2)) ); - } - } - - double k3 = compute_exact_linear_pwg(PD1,PD2,sigma,C,p); - return std::sqrt(k1+k2-2*k3); - -} - -double compute_approximate_linear_pwg_from_Fourier_features(const std::vector >& B1, \ - const std::vector >& B2){ - double d = 0; int M = B1.size(); - for(int i = 0; i < M; i++) d += B1[i].first*B2[i].first + B1[i].second*B2[i].second; - return (1.0/M)*d; -} - -double compute_approximate_gaussian_pwg_from_Fourier_features(const std::vector >& B1, \ - const std::vector >& B2, double tau){ - int M = B1.size(); - double d3 = compute_approximate_linear_pwg_from_Fourier_features(B1, B2); - double d1 = 0; double d2 = 0; - for(int i = 0; i < M; i++){d1 += pow(B1[i].first,2) + pow(B1[i].second,2); d2 += pow(B2[i].first,2) + pow(B2[i].second,2);} - return exp( -((1.0/M)*(d1+d2)-2*d3) / (2*pow(tau,2)) ); -} - -double compute_approximate_gaussian_RKHSdist_from_Fourier_features(const std::vector >& B1, \ - const std::vector >& B2){ - int M = B1.size(); - double d3 = compute_approximate_linear_pwg_from_Fourier_features(B1, B2); - double d1 = 0; double d2 = 0; - for(int i = 0; i < M; i++){d1 += pow(B1[i].first,2) + pow(B1[i].second,2); d2 += pow(B2[i].first,2) + pow(B2[i].second,2);} - return std::sqrt((1.0/M)*(d1+d2)-2*d3); -} - -std::vector > compute_Fourier_features(double C, int p, PD D, std::vector > Z){ - int m = D.size(); std::vector > B; int M = Z.size(); - for(int i = 0; i < M; i++){ - double d1 = 0; double d2 = 0; double zx = Z[i].first; double zy = Z[i].second; - for(int j = 0; j < m; j++){ - double x = D[j].first; double y = D[j].second; - d1 += atan(C*pow(y-x,p))*cos(x*zx + y*zy); - d2 += atan(C*pow(y-x,p))*sin(x*zx + y*zy); - } - B.push_back(std::pair(d1,d2)); - } - return B; -} - -std::vector > random_Fourier(double sigma, int M = 1000){ - std::normal_distribution distrib(0,1); std::vector > Z; - std::random_device rd; - for(int i = 0; i < M; i++){ - //unsigned seedx = 2*i; unsigned seedy = 2*i+1; - //std::default_random_engine generatorx(seedx); std::default_random_engine generatory(seedy); - std::mt19937 e1(rd()); std::mt19937 e2(rd()); - double zx = distrib(e1/*generatorx*/); double zy = distrib(e2/*generatory*/); - Z.push_back(std::pair((1/sigma)*zx,(1/sigma)*zy)); - } - return Z; -} - -double compute_approximate_linear_pwg(PD PD1, PD PD2, double sigma, double C, int p, int M = 1000){ - std::vector > Z = random_Fourier(sigma, M); - std::vector > B1 = compute_Fourier_features(C,p,PD1,Z); - std::vector > B2 = compute_Fourier_features(C,p,PD2,Z); - return compute_approximate_linear_pwg_from_Fourier_features(B1,B2); -} - -double compute_approximate_gaussian_pwg(PD PD1, PD PD2, double sigma, double C, int p, double tau, int M = 1000){ - std::vector > Z = random_Fourier(sigma, M); - std::vector > B1 = compute_Fourier_features(C,p,PD1,Z); - std::vector > B2 = compute_Fourier_features(C,p,PD2,Z); - return compute_approximate_gaussian_pwg_from_Fourier_features(B1,B2,tau); -} - - -} // namespace persistence_weighted_gaussian - -} // namespace Gudhi - -#endif //PWG_H_ diff --git a/src/Kernels/include/gudhi/SW.h b/src/Kernels/include/gudhi/SW.h deleted file mode 100644 index 0b041252..00000000 --- a/src/Kernels/include/gudhi/SW.h +++ /dev/null @@ -1,316 +0,0 @@ -/* 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): Mathieu Carrière - * - * Copyright (C) 2017 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 SW_H_ -#define SW_H_ - -#define NUMPI 3.14159265359 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using PD = std::vector >; - -bool sortAngle(const std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);} -bool myComp(const std::pair & P1, const std::pair & P2){return P1.second < P2.second;} - -namespace Gudhi { -namespace sliced_wasserstein { - - -// ******************************************************************** -// Approximate computation. -// ******************************************************************** - -/** \brief Computes an approximation of the Sliced Wasserstein distance between two persistence diagrams - * - * @param[in] N number of points sampled on the circle. - * - */ - -double compute_approximate_SW(PD PD1, PD PD2, int N = 100){ - - double step = NUMPI/N; double sw = 0; - - // Add projections onto diagonal. - int n1, n2; n1 = PD1.size(); n2 = PD2.size(); - for (int i = 0; i < n2; i++) - PD1.push_back(std::pair( (PD2[i].first + PD2[i].second)/2, (PD2[i].first + PD2[i].second)/2) ); - for (int i = 0; i < n1; i++) - PD2.push_back(std::pair( (PD1[i].first + PD1[i].second)/2, (PD1[i].first + PD1[i].second)/2) ); - int n = PD1.size(); - - // Sort and compare all projections. - //#pragma omp parallel for - for (int i = 0; i < N; i++){ - std::vector > L1, L2; - for (int j = 0; j < n; j++){ - L1.push_back( std::pair(j, PD1[j].first*cos(-NUMPI/2+i*step) + PD1[j].second*sin(-NUMPI/2+i*step)) ); - L2.push_back( std::pair(j, PD2[j].first*cos(-NUMPI/2+i*step) + PD2[j].second*sin(-NUMPI/2+i*step)) ); - } - std::sort(L1.begin(),L1.end(), myComp); std::sort(L2.begin(),L2.end(), myComp); - double f = 0; for (int j = 0; j < n; j++) f += std::abs(L1[j].second - L2[j].second); - sw += f*step; - } - return sw/NUMPI; -} - - - - - - - - - - - - - - - - - - - -// ******************************************************************** -// Exact computation. -// ******************************************************************** - - - -// Compute the angle formed by two points of a PD -double compute_angle(const PD & PersDiag, const int & i, const int & j){ - std::pair vect; double x1,y1, x2,y2; - x1 = PersDiag[i].first; y1 = PersDiag[i].second; - x2 = PersDiag[j].first; y2 = PersDiag[j].second; - if (y1 - y2 > 0){ - vect.first = y1 - y2; - vect.second = x2 - x1;} - else{ - if(y1 - y2 < 0){ - vect.first = y2 - y1; - vect.second = x1 - x2; - } - else{ - vect.first = 0; - vect.second = abs(x1 - x2);} - } - double norm = std::sqrt(pow(vect.first,2) + pow(vect.second,2)); - return asin(vect.second/norm); -} - -// Compute the integral of |cos()| between alpha and beta -// Valid only if alpha is in [-pi,pi] and beta-alpha is in [0,pi] -double compute_int_cos(const double & alpha, const double & beta){ - double res = 0; - assert((alpha >= 0 && alpha <= NUMPI) || (alpha >= -NUMPI && alpha <= 0)); - if (alpha >= 0 && alpha <= NUMPI){ - if (cos(alpha) >= 0){ - if(NUMPI/2 <= beta){res = 2-sin(alpha)-sin(beta);} - else{res = sin(beta)-sin(alpha);} - } - else{ - if(1.5*NUMPI <= beta){res = 2+sin(alpha)+sin(beta);} - else{res = sin(alpha)-sin(beta);} - } - } - if (alpha >= -NUMPI && alpha <= 0){ - if (cos(alpha) <= 0){ - if(-NUMPI/2 <= beta){res = 2+sin(alpha)+sin(beta);} - else{res = sin(alpha)-sin(beta);} - } - else{ - if(NUMPI/2 <= beta){res = 2-sin(alpha)-sin(beta);} - else{res = sin(beta)-sin(alpha);} - } - } - return res; -} - -double compute_int(const double & theta1, const double & theta2, const int & p, const int & q, const PD & PD1, const PD & PD2){ - double norm = std::sqrt(pow(PD1[p].first-PD2[q].first,2) + pow(PD1[p].second-PD2[q].second,2)); - double angle1; - if (PD1[p].first > PD2[q].first) - angle1 = theta1 - asin( (PD1[p].second-PD2[q].second)/norm ); - else - angle1 = theta1 - asin( (PD2[q].second-PD1[p].second)/norm ); - double angle2 = angle1 + theta2 - theta1; - double integral = compute_int_cos(angle1,angle2); - return norm*integral; -} - - - -double compute_sw(const std::vector > > & V1, const std::vector > > & V2, const PD & PD1, const PD & PD2){ - int N = V1.size(); double sw = 0; - for (int i = 0; i < N; i++){ - std::vector > U,V; U = V1[i]; V = V2[i]; - double theta1, theta2; theta1 = -NUMPI/2; - unsigned int ku, kv; ku = 0; kv = 0; theta2 = std::min(U[ku].second,V[kv].second); - while(theta1 != NUMPI/2){ - if(PD1[U[ku].first].first != PD2[V[kv].first].first || PD1[U[ku].first].second != PD2[V[kv].first].second) - if(theta1 != theta2) - sw += Gudhi::sliced_wasserstein::compute_int(theta1, theta2, U[ku].first, V[kv].first, PD1, PD2); - theta1 = theta2; - if ( (theta2 == U[ku].second) && ku < U.size()-1 ) ku++; - if ( (theta2 == V[kv].second) && kv < V.size()-1 ) kv++; - theta2 = std::min(U[ku].second, V[kv].second); - } - } - return sw/NUMPI; -} - - - - - - - - - - - - - - - - - - - -/** \brief Computes the Sliced Wasserstein distance between two persistence diagrams - * - */ - -double compute_exact_SW(PD PD1, PD PD2){ - - // Add projections onto diagonal. - int n1, n2; n1 = PD1.size(); n2 = PD2.size(); double max_ordinate = std::numeric_limits::lowest(); - for (int i = 0; i < n2; i++){ - max_ordinate = std::max(max_ordinate, PD2[i].second); - PD1.push_back( std::pair( ((PD2[i].first+PD2[i].second)/2), ((PD2[i].first+PD2[i].second)/2) ) ); - } - for (int i = 0; i < n1; i++){ - max_ordinate = std::max(max_ordinate, PD1[i].second); - PD2.push_back( std::pair( ((PD1[i].first+PD1[i].second)/2), ((PD1[i].first+PD1[i].second)/2) ) ); - } - int N = PD1.size(); assert(N==PD2.size()); - - // Slightly perturb the points so that the PDs are in generic positions. - int mag = 0; while(max_ordinate > 10){mag++; max_ordinate/=10;} - double thresh = pow(10,-5+mag); - srand(time(NULL)); - for (int i = 0; i < N; i++){ - PD1[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); PD1[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); - PD2[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); PD2[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); - } - - // Compute all angles in both PDs. - std::vector > > angles1, angles2; - for (int i = 0; i < N; i++){ - for (int j = i+1; j < N; j++){ - double theta1 = Gudhi::sliced_wasserstein::compute_angle(PD1,i,j); double theta2 = Gudhi::sliced_wasserstein::compute_angle(PD2,i,j); - angles1.push_back(std::pair >(theta1, std::pair(i,j))); - angles2.push_back(std::pair >(theta2, std::pair(i,j))); - } - } - - // Sort angles. - std::sort(angles1.begin(), angles1.end(), sortAngle); std::sort(angles2.begin(), angles2.end(), sortAngle); - - // Initialize orders of the points of both PDs (given by ordinates when theta = -pi/2). - std::vector orderp1, orderp2; - for (int i = 0; i < N; i++){ orderp1.push_back(i); orderp2.push_back(i); } - std::sort( orderp1.begin(), orderp1.end(), [=](int i, int j){ if(PD1[i].second != PD1[j].second) return (PD1[i].second < PD1[j].second); else return (PD1[i].first > PD1[j].first); } ); - std::sort( orderp2.begin(), orderp2.end(), [=](int i, int j){ if(PD2[i].second != PD2[j].second) return (PD2[i].second < PD2[j].second); else return (PD2[i].first > PD2[j].first); } ); - - // Find the inverses of the orders. - std::vector order1(N); std::vector order2(N); - for(int i = 0; i < N; i++) for (int j = 0; j < N; j++) if(orderp1[j] == i){ order1[i] = j; break; } - for(int i = 0; i < N; i++) for (int j = 0; j < N; j++) if(orderp2[j] == i){ order2[i] = j; break; } - - // Record all inversions of points in the orders as theta varies along the positive half-disk. - std::vector > > anglePerm1(N); - std::vector > > anglePerm2(N); - - int M1 = angles1.size(); - for (int i = 0; i < M1; i++){ - double theta = angles1[i].first; int p = angles1[i].second.first; int q = angles1[i].second.second; - anglePerm1[order1[p]].push_back(std::pair(p,theta)); - anglePerm1[order1[q]].push_back(std::pair(q,theta)); - int a = order1[p]; int b = order1[q]; order1[p] = b; order1[q] = a; - } - - int M2 = angles2.size(); - for (int i = 0; i < M2; i++){ - double theta = angles2[i].first; int p = angles2[i].second.first; int q = angles2[i].second.second; - anglePerm2[order2[p]].push_back(std::pair(p,theta)); - anglePerm2[order2[q]].push_back(std::pair(q,theta)); - int a = order2[p]; int b = order2[q]; order2[p] = b; order2[q] = a; - } - - for (int i = 0; i < N; i++){ - anglePerm1[order1[i]].push_back(std::pair(i,NUMPI/2)); - anglePerm2[order2[i]].push_back(std::pair(i,NUMPI/2)); - } - - // Compute the SW distance with the list of inversions. - return Gudhi::sliced_wasserstein::compute_sw(anglePerm1, anglePerm2, PD1, PD2); - -} - - - - - - -}} - -#endif - - diff --git a/src/Kernels/include/gudhi/figtree-0.9.3.zip b/src/Kernels/include/gudhi/figtree-0.9.3.zip deleted file mode 100644 index a9468274..00000000 Binary files a/src/Kernels/include/gudhi/figtree-0.9.3.zip and /dev/null differ diff --git a/src/Kernels/include/gudhi/kernel.h b/src/Kernels/include/gudhi/kernel.h new file mode 100644 index 00000000..c4120d7a --- /dev/null +++ b/src/Kernels/include/gudhi/kernel.h @@ -0,0 +1,447 @@ +/* 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): Mathieu Carrière + * + * Copyright (C) 2017 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 KERNEL_H_ +#define KERNEL_H_ + +#define NUMPI 3.14159265359 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using PD = std::vector >; +bool sortAngle(const std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);} +bool myComp(const std::pair & P1, const std::pair & P2){return P1.second < P2.second;} + +namespace Gudhi { +namespace kernel { + + +double pss_weight(std::pair P){ + if(P.second > P.first) return 1; + else return -1; +} + + + + +// ******************************************************************** +// Exact computation. +// ******************************************************************** + +/** \brief Computes the Linear Persistence Weighted Gaussian Kernel between two persistence diagrams. + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * @param[in] sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. + * @param[in] weight weight function for the points in the diagrams. + * + */ +double lpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ + int num_pts1 = PD1.size(); int num_pts2 = PD2.size(); double k = 0; + for(int i = 0; i < num_pts1; i++) + for(int j = 0; j < num_pts2; j++) + k += (*weight)(PD1[i])*(*weight)(PD2[j])*exp(-(pow(PD1[i].first-PD2[j].first,2) + pow(PD1[i].second-PD2[j].second,2))/(2*pow(sigma,2))); + return k; +} + +/** \brief Computes the Persistence Scale Space Kernel between two persistence diagrams. + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * @param[in] sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. + * + */ +double pss(PD PD1, PD PD2, double sigma){ + PD pd1 = PD1; int numpts = PD1.size(); for(int i = 0; i < numpts; i++) pd1.push_back(std::pair(PD1[i].second,PD1[i].first)); + PD pd2 = PD2; numpts = PD2.size(); for(int i = 0; i < numpts; i++) pd2.push_back(std::pair(PD2[i].second,PD2[i].first)); + return lpwg(pd1, pd2, 2*sqrt(sigma), &pss_weight) / (2*8*NUMPI*sigma); +} + +/** \brief Computes the Gaussian Persistence Weighted Gaussian Kernel between two persistence diagrams. + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * @param[in] sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. + * @param[in] tau bandwidth parameter of the Gaussian Kernel used between the embeddings. + * @param[in] weight weight function for the points in the diagrams. + * + */ +double gpwg(PD PD1, PD PD2, double sigma, double tau, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ + double k1 = lpwg(PD1,PD1,sigma,weight); + double k2 = lpwg(PD2,PD2,sigma,weight); + double k3 = lpwg(PD1,PD2,sigma,weight); + return exp( - (k1+k2-2*k3) / (2*pow(tau,2)) ); +} + +/** \brief Computes the RKHS distance induced by the Gaussian Kernel Embedding between two persistence diagrams. + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * @param[in] sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. + * @param[in] weight weight function for the points in the diagrams. + * + */ +double dpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ + double k1 = lpwg(PD1,PD1,sigma,weight); + double k2 = lpwg(PD2,PD2,sigma,weight); + double k3 = lpwg(PD1,PD2,sigma,weight); + return std::sqrt(k1+k2-2*k3); +} + +// Compute the angle formed by two points of a PD +double compute_angle(const PD & PersDiag, const int & i, const int & j){ + std::pair vect; double x1,y1, x2,y2; + x1 = PersDiag[i].first; y1 = PersDiag[i].second; + x2 = PersDiag[j].first; y2 = PersDiag[j].second; + if (y1 - y2 > 0){ + vect.first = y1 - y2; + vect.second = x2 - x1;} + else{ + if(y1 - y2 < 0){ + vect.first = y2 - y1; + vect.second = x1 - x2; + } + else{ + vect.first = 0; + vect.second = abs(x1 - x2);} + } + double norm = std::sqrt(pow(vect.first,2) + pow(vect.second,2)); + return asin(vect.second/norm); +} + +// Compute the integral of |cos()| between alpha and beta +// Valid only if alpha is in [-pi,pi] and beta-alpha is in [0,pi] +double compute_int_cos(const double & alpha, const double & beta){ + double res = 0; + assert((alpha >= 0 && alpha <= NUMPI) || (alpha >= -NUMPI && alpha <= 0)); + if (alpha >= 0 && alpha <= NUMPI){ + if (cos(alpha) >= 0){ + if(NUMPI/2 <= beta){res = 2-sin(alpha)-sin(beta);} + else{res = sin(beta)-sin(alpha);} + } + else{ + if(1.5*NUMPI <= beta){res = 2+sin(alpha)+sin(beta);} + else{res = sin(alpha)-sin(beta);} + } + } + if (alpha >= -NUMPI && alpha <= 0){ + if (cos(alpha) <= 0){ + if(-NUMPI/2 <= beta){res = 2+sin(alpha)+sin(beta);} + else{res = sin(alpha)-sin(beta);} + } + else{ + if(NUMPI/2 <= beta){res = 2-sin(alpha)-sin(beta);} + else{res = sin(beta)-sin(alpha);} + } + } + return res; +} + +double compute_int(const double & theta1, const double & theta2, const int & p, const int & q, const PD & PD1, const PD & PD2){ + double norm = std::sqrt(pow(PD1[p].first-PD2[q].first,2) + pow(PD1[p].second-PD2[q].second,2)); + double angle1; + if (PD1[p].first > PD2[q].first) + angle1 = theta1 - asin( (PD1[p].second-PD2[q].second)/norm ); + else + angle1 = theta1 - asin( (PD2[q].second-PD1[p].second)/norm ); + double angle2 = angle1 + theta2 - theta1; + double integral = compute_int_cos(angle1,angle2); + return norm*integral; +} + + + +double compute_sw(const std::vector > > & V1, const std::vector > > & V2, const PD & PD1, const PD & PD2){ + int N = V1.size(); double sw = 0; + for (int i = 0; i < N; i++){ + std::vector > U,V; U = V1[i]; V = V2[i]; + double theta1, theta2; theta1 = -NUMPI/2; + unsigned int ku, kv; ku = 0; kv = 0; theta2 = std::min(U[ku].second,V[kv].second); + while(theta1 != NUMPI/2){ + if(PD1[U[ku].first].first != PD2[V[kv].first].first || PD1[U[ku].first].second != PD2[V[kv].first].second) + if(theta1 != theta2) + sw += compute_int(theta1, theta2, U[ku].first, V[kv].first, PD1, PD2); + theta1 = theta2; + if ( (theta2 == U[ku].second) && ku < U.size()-1 ) ku++; + if ( (theta2 == V[kv].second) && kv < V.size()-1 ) kv++; + theta2 = std::min(U[ku].second, V[kv].second); + } + } + return sw/NUMPI; +} + +/** \brief Computes the Sliced Wasserstein distance between two persistence diagrams. + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * + */ + double sw(PD PD1, PD PD2){ + + // Add projections onto diagonal. + int n1, n2; n1 = PD1.size(); n2 = PD2.size(); double max_ordinate = std::numeric_limits::lowest(); + for (int i = 0; i < n2; i++){ + max_ordinate = std::max(max_ordinate, PD2[i].second); + PD1.push_back( std::pair( ((PD2[i].first+PD2[i].second)/2), ((PD2[i].first+PD2[i].second)/2) ) ); + } + for (int i = 0; i < n1; i++){ + max_ordinate = std::max(max_ordinate, PD1[i].second); + PD2.push_back( std::pair( ((PD1[i].first+PD1[i].second)/2), ((PD1[i].first+PD1[i].second)/2) ) ); + } + int N = PD1.size(); assert(N==PD2.size()); + + // Slightly perturb the points so that the PDs are in generic positions. + int mag = 0; while(max_ordinate > 10){mag++; max_ordinate/=10;} + double thresh = pow(10,-5+mag); + srand(time(NULL)); + for (int i = 0; i < N; i++){ + PD1[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); PD1[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); + PD2[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); PD2[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); + } + + // Compute all angles in both PDs. + std::vector > > angles1, angles2; + for (int i = 0; i < N; i++){ + for (int j = i+1; j < N; j++){ + double theta1 = compute_angle(PD1,i,j); double theta2 = compute_angle(PD2,i,j); + angles1.push_back(std::pair >(theta1, std::pair(i,j))); + angles2.push_back(std::pair >(theta2, std::pair(i,j))); + } + } + + // Sort angles. + std::sort(angles1.begin(), angles1.end(), sortAngle); std::sort(angles2.begin(), angles2.end(), sortAngle); + + // Initialize orders of the points of both PDs (given by ordinates when theta = -pi/2). + std::vector orderp1, orderp2; + for (int i = 0; i < N; i++){ orderp1.push_back(i); orderp2.push_back(i); } + std::sort( orderp1.begin(), orderp1.end(), [=](int i, int j){ if(PD1[i].second != PD1[j].second) return (PD1[i].second < PD1[j].second); else return (PD1[i].first > PD1[j].first); } ); + std::sort( orderp2.begin(), orderp2.end(), [=](int i, int j){ if(PD2[i].second != PD2[j].second) return (PD2[i].second < PD2[j].second); else return (PD2[i].first > PD2[j].first); } ); + + // Find the inverses of the orders. + std::vector order1(N); std::vector order2(N); + for(int i = 0; i < N; i++) for (int j = 0; j < N; j++) if(orderp1[j] == i){ order1[i] = j; break; } + for(int i = 0; i < N; i++) for (int j = 0; j < N; j++) if(orderp2[j] == i){ order2[i] = j; break; } + + // Record all inversions of points in the orders as theta varies along the positive half-disk. + std::vector > > anglePerm1(N); + std::vector > > anglePerm2(N); + + int M1 = angles1.size(); + for (int i = 0; i < M1; i++){ + double theta = angles1[i].first; int p = angles1[i].second.first; int q = angles1[i].second.second; + anglePerm1[order1[p]].push_back(std::pair(p,theta)); + anglePerm1[order1[q]].push_back(std::pair(q,theta)); + int a = order1[p]; int b = order1[q]; order1[p] = b; order1[q] = a; + } + + int M2 = angles2.size(); + for (int i = 0; i < M2; i++){ + double theta = angles2[i].first; int p = angles2[i].second.first; int q = angles2[i].second.second; + anglePerm2[order2[p]].push_back(std::pair(p,theta)); + anglePerm2[order2[q]].push_back(std::pair(q,theta)); + int a = order2[p]; int b = order2[q]; order2[p] = b; order2[q] = a; + } + + for (int i = 0; i < N; i++){ + anglePerm1[order1[i]].push_back(std::pair(i,NUMPI/2)); + anglePerm2[order2[i]].push_back(std::pair(i,NUMPI/2)); + } + + // Compute the SW distance with the list of inversions. + return compute_sw(anglePerm1, anglePerm2, PD1, PD2); + +} + + + + + + + + + +// ******************************************************************** +// Approximate computation. +// ******************************************************************** + +double approx_lpwg_Fourier(const std::vector >& B1, const std::vector >& B2){ + double d = 0; int M = B1.size(); + for(int i = 0; i < M; i++) d += B1[i].first*B2[i].first + B1[i].second*B2[i].second; + return (1.0/M)*d; +} + +double approx_gpwg_Fourier(const std::vector >& B1, const std::vector >& B2, double tau){ + int M = B1.size(); + double d3 = approx_lpwg_Fourier(B1, B2); + double d1 = 0; double d2 = 0; + for(int i = 0; i < M; i++){d1 += pow(B1[i].first,2) + pow(B1[i].second,2); d2 += pow(B2[i].first,2) + pow(B2[i].second,2);} + return exp( -((1.0/M)*(d1+d2)-2*d3) / (2*pow(tau,2)) ); +} + +double approx_dpwg_Fourier(const std::vector >& B1, const std::vector >& B2){ + int M = B1.size(); + double d3 = approx_lpwg_Fourier(B1, B2); + double d1 = 0; double d2 = 0; + for(int i = 0; i < M; i++){d1 += pow(B1[i].first,2) + pow(B1[i].second,2); d2 += pow(B2[i].first,2) + pow(B2[i].second,2);} + return std::sqrt((1.0/M)*(d1+d2)-2*d3); +} + +std::vector > Fourier_feat(PD D, std::vector > Z, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ + int m = D.size(); std::vector > B; int M = Z.size(); + for(int i = 0; i < M; i++){ + double d1 = 0; double d2 = 0; double zx = Z[i].first; double zy = Z[i].second; + for(int j = 0; j < m; j++){ + double x = D[j].first; double y = D[j].second; + d1 += (*weight)(D[j])*cos(x*zx + y*zy); + d2 += (*weight)(D[j])*sin(x*zx + y*zy); + } + B.push_back(std::pair(d1,d2)); + } + return B; +} + +std::vector > random_Fourier(double sigma, int M = 1000){ + std::normal_distribution distrib(0,1); std::vector > Z; std::random_device rd; + for(int i = 0; i < M; i++){ + std::mt19937 e1(rd()); std::mt19937 e2(rd()); + double zx = distrib(e1); double zy = distrib(e2); + Z.push_back(std::pair((1.0/sigma)*zx,(1.0/sigma)*zy)); + } + return Z; +} + + +/** \brief Computes an approximation of the Linear Persistence Weighted Gaussian Kernel between two persistence diagrams with random Fourier features. + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * @param[in] sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. + * @param[in] weight weight function for the points in the diagrams. + * @param[in] M number of Fourier features. + * + */ +double approx_lpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}, int M = 1000){ + std::vector > Z = random_Fourier(sigma, M); + std::vector > B1 = Fourier_feat(PD1,Z,weight); + std::vector > B2 = Fourier_feat(PD2,Z,weight); + return approx_lpwg_Fourier(B1,B2); +} + +/** \brief Computes an approximation of the Persistence Scale Space Kernel between two persistence diagrams with random Fourier features. + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * @param[in] sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. + * @param[in] M number of Fourier features. + * + */ +double approx_pss(PD PD1, PD PD2, double sigma, int M = 1000){ + PD pd1 = PD1; int numpts = PD1.size(); for(int i = 0; i < numpts; i++) pd1.push_back(std::pair(PD1[i].second,PD1[i].first)); + PD pd2 = PD2; numpts = PD2.size(); for(int i = 0; i < numpts; i++) pd2.push_back(std::pair(PD2[i].second,PD2[i].first)); + return approx_lpwg(pd1, pd2, 2*sqrt(sigma), &pss_weight, M) / (2*8*NUMPI*sigma); +} + + +/** \brief Computes an approximation of the Gaussian Persistence Weighted Gaussian Kernel between two persistence diagrams with random Fourier features. + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * @param[in] sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. + * @param[in] tau bandwidth parameter of the Gaussian Kernel used between the embeddings. + * @param[in] weight weight function for the points in the diagrams. + * @param[in] M number of Fourier features. + * + */ +double approx_gpwg(PD PD1, PD PD2, double sigma, double tau, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}, int M = 1000){ + std::vector > Z = random_Fourier(sigma, M); + std::vector > B1 = Fourier_feat(PD1,Z,weight); + std::vector > B2 = Fourier_feat(PD2,Z,weight); + return approx_gpwg_Fourier(B1,B2,tau); +} + + +/** \brief Computes an approximation of the Sliced Wasserstein distance between two persistence diagrams. + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * @param[in] N number of points sampled on the circle. + * + */ +double approx_sw(PD PD1, PD PD2, int N = 100){ + + double step = NUMPI/N; double sw = 0; + + // Add projections onto diagonal. + int n1, n2; n1 = PD1.size(); n2 = PD2.size(); + for (int i = 0; i < n2; i++) + PD1.push_back(std::pair( (PD2[i].first + PD2[i].second)/2, (PD2[i].first + PD2[i].second)/2) ); + for (int i = 0; i < n1; i++) + PD2.push_back(std::pair( (PD1[i].first + PD1[i].second)/2, (PD1[i].first + PD1[i].second)/2) ); + int n = PD1.size(); + + // Sort and compare all projections. + //#pragma omp parallel for + for (int i = 0; i < N; i++){ + std::vector > L1, L2; + for (int j = 0; j < n; j++){ + L1.push_back( std::pair(j, PD1[j].first*cos(-NUMPI/2+i*step) + PD1[j].second*sin(-NUMPI/2+i*step)) ); + L2.push_back( std::pair(j, PD2[j].first*cos(-NUMPI/2+i*step) + PD2[j].second*sin(-NUMPI/2+i*step)) ); + } + std::sort(L1.begin(),L1.end(), myComp); std::sort(L2.begin(),L2.end(), myComp); + double f = 0; for (int j = 0; j < n; j++) f += std::abs(L1[j].second - L2[j].second); + sw += f*step; + } + return sw/NUMPI; +} + + + +} // namespace kernel + +} // namespace Gudhi + +#endif //KERNEL_H_ -- cgit v1.2.3 From 5708c93251625133598739f42ed106aac83bf18a Mon Sep 17 00:00:00 2001 From: mcarrier Date: Fri, 29 Dec 2017 23:39:32 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3107 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: d0f6606d7afde132a4d86203eeff80e97f35adce --- src/Kernels/doc/COPYRIGHT | 19 ++++++++++++ src/Kernels/doc/Intro_kernels.h | 61 ++++++++++++++++++++++++++++++++++++++ src/Kernels/include/gudhi/kernel.h | 9 ++++++ 3 files changed, 89 insertions(+) create mode 100644 src/Kernels/doc/COPYRIGHT create mode 100644 src/Kernels/doc/Intro_kernels.h diff --git a/src/Kernels/doc/COPYRIGHT b/src/Kernels/doc/COPYRIGHT new file mode 100644 index 00000000..0c36a526 --- /dev/null +++ b/src/Kernels/doc/COPYRIGHT @@ -0,0 +1,19 @@ +The files of this directory are part of the Gudhi Library. The Gudhi library +(Geometric Understanding in Higher Dimensions) is a generic C++ library for +computational topology. + +Author(s): Mathieu Carrière + +Copyright (C) 2017 INRIA + +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 . diff --git a/src/Kernels/doc/Intro_kernels.h b/src/Kernels/doc/Intro_kernels.h new file mode 100644 index 00000000..be97a6cf --- /dev/null +++ b/src/Kernels/doc/Intro_kernels.h @@ -0,0 +1,61 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2017 INRIA + * + * 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 DOC_KERNEL_INTRO_KERNEL_H_ +#define DOC_KERNEL_INTRO_KERNEL_H_ + +namespace Gudhi { + +namespace kernel { + +/** \defgroup kernel Kernels + * + * \author Mathieu Carrière + * + * @{ + * + * Kernels are generalized scalar products. They take the form of functions whose evaluations on pairs of persistence diagrams are equal + * to the scalar products of the images of the diagrams under some feature map into a (generally unknown and infinite dimensional) + * Hilbert space. Kernels are + * very useful to handle any type of data for algorithms that require at least a Hilbert structure, such as Principal Component Analysis + * or Support Vector Machines. In this package, we implement three kernels for persistence diagrams: the Persistence Scale Space kernel, + * the Persistence Weighted Gaussian kernel and the Sliced Wasserstein kernel. + * + * + * When launching: + * + * \code $> ./BasicEx + * \endcode + * + * the program output is: + * + * + * \copyright GNU General Public License v3. + * \verbatim Contact: gudhi-users@lists.gforge.inria.fr \endverbatim + */ +/** @} */ // end defgroup kernel + +} // namespace kernel + +} // namespace Gudhi + +#endif // DOC_KERNEL_INTRO_KERNEL_H_ diff --git a/src/Kernels/include/gudhi/kernel.h b/src/Kernels/include/gudhi/kernel.h index c4120d7a..6429efed 100644 --- a/src/Kernels/include/gudhi/kernel.h +++ b/src/Kernels/include/gudhi/kernel.h @@ -72,6 +72,7 @@ double pss_weight(std::pair P){ // ******************************************************************** /** \brief Computes the Linear Persistence Weighted Gaussian Kernel between two persistence diagrams. + * \ingroup kernel * * @param[in] PD1 first persistence diagram. * @param[in] PD2 second persistence diagram. @@ -88,6 +89,7 @@ double lpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair > > & V1, } /** \brief Computes the Sliced Wasserstein distance between two persistence diagrams. + * \ingroup kernel * * @param[in] PD1 first persistence diagram. * @param[in] PD2 second persistence diagram. @@ -356,6 +361,7 @@ std::vector > random_Fourier(double sigma, int M = 1000 /** \brief Computes an approximation of the Linear Persistence Weighted Gaussian Kernel between two persistence diagrams with random Fourier features. + * \ingroup kernel * * @param[in] PD1 first persistence diagram. * @param[in] PD2 second persistence diagram. @@ -372,6 +378,7 @@ double approx_lpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair Date: Wed, 3 Jan 2018 15:30:49 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3110 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 95fd5b9e4cd4738ecdeaeb27f20d267816b7bead --- biblio/bibliography.bib | 22 +++++++ data/persistence_diagram/PD1 | 3 + data/persistence_diagram/PD2 | 2 + src/Kernels/doc/Intro_kernels.h | 53 ++++++++++++++++- src/Kernels/example/kernel.txt | 8 +++ src/Kernels/example/kernel_basic_example.cpp | 48 ++++++++++------ src/Kernels/include/gudhi/kernel.h | 86 +++++++++++++++++----------- src/Kernels/test/CMakeLists.txt | 14 +++++ src/Kernels/test/test_kernel.cpp | 56 ++++++++++++++++++ 9 files changed, 239 insertions(+), 53 deletions(-) create mode 100644 data/persistence_diagram/PD1 create mode 100644 data/persistence_diagram/PD2 create mode 100644 src/Kernels/example/kernel.txt create mode 100644 src/Kernels/test/CMakeLists.txt create mode 100644 src/Kernels/test/test_kernel.cpp diff --git a/biblio/bibliography.bib b/biblio/bibliography.bib index b101cb76..e56734e4 100644 --- a/biblio/bibliography.bib +++ b/biblio/bibliography.bib @@ -1072,3 +1072,25 @@ language={English} + +@InProceedings{pmlr-v70-carriere17a, + title = {Sliced {W}asserstein Kernel for Persistence Diagrams}, + author = {Mathieu Carri{\`e}re and Marco Cuturi and Steve Oudot}, + booktitle = {Proceedings of the 34th International Conference on Machine Learning}, + pages = {664--673}, + year = {2017}, + editor = {Doina Precup and Yee Whye Teh}, + volume = {70}, + series = {Proceedings of Machine Learning Research}, + address = {International Convention Centre, Sydney, Australia}, + month = {06--11 Aug}, + publisher = {PMLR}, +} + +@INPROCEEDINGS{Rahimi07randomfeatures, + author = {Ali Rahimi and Ben Recht}, + title = {Random features for large-scale kernel machines}, + booktitle = {In Neural Information Processing Systems}, + year = {2007} +} + diff --git a/data/persistence_diagram/PD1 b/data/persistence_diagram/PD1 new file mode 100644 index 00000000..404199b4 --- /dev/null +++ b/data/persistence_diagram/PD1 @@ -0,0 +1,3 @@ +2.7 3.7 +9.6 14 +34.2 34.974 \ No newline at end of file diff --git a/data/persistence_diagram/PD2 b/data/persistence_diagram/PD2 new file mode 100644 index 00000000..125d8e4b --- /dev/null +++ b/data/persistence_diagram/PD2 @@ -0,0 +1,2 @@ +2.8 4.45 +9.5 14.1 \ No newline at end of file diff --git a/src/Kernels/doc/Intro_kernels.h b/src/Kernels/doc/Intro_kernels.h index be97a6cf..163690b1 100644 --- a/src/Kernels/doc/Intro_kernels.h +++ b/src/Kernels/doc/Intro_kernels.h @@ -37,17 +37,64 @@ namespace kernel { * to the scalar products of the images of the diagrams under some feature map into a (generally unknown and infinite dimensional) * Hilbert space. Kernels are * very useful to handle any type of data for algorithms that require at least a Hilbert structure, such as Principal Component Analysis - * or Support Vector Machines. In this package, we implement three kernels for persistence diagrams: the Persistence Scale Space kernel, - * the Persistence Weighted Gaussian kernel and the Sliced Wasserstein kernel. + * or Support Vector Machines. In this package, we implement three kernels for persistence diagrams: + * the Persistence Scale Space Kernel (PSSK)---see \cite Reininghaus_Huber_ALL_PSSK, + * the Persistence Weighted Gaussian Kernel (PWGK)---see \cite Kusano_Fukumizu_Hiraoka_PWGK, + * and the Sliced Wasserstein Kernel (SWK)---see \cite pmlr-v70-carriere17a. * + * \section pwg Persistence Weighted Gaussian Kernel and Persistence Scale Space Kernel + * + * The PWGK is built with Gaussian Kernel Mean Embedding, meaning that each persistence diagram is first + * sent to the Hilbert space of a Gaussian kernel with bandwidth parameter \f$\sigma >0\f$ using a weighted mean embedding \f$\Phi\f$: + * + * \f$ \Phi\,:\,D\,\rightarrow\,\sum_{p\in D}\,w(p)\,{\rm exp}\left(-\frac{\|p-\cdot\|_2^2}{2\sigma^2}\right) \f$, + * + * Usually, the weight function is chosen to be an arctan function of the distance of the point to the diagonal: + * \f$w(p) = {\rm arctan}(C\,|y-x|^\alpha)\f$, for some parameters \f$C,\alpha >0\f$. + * Then, either their scalar product in this space is + * computed (Linear Persistence Weighted Gaussian Kernel): + * + * \f$ LPWGK(D_1,D_2)=\langle\Phi(D_1),\Phi(D_2)\rangle + * \,=\,\sum_{p\in D_1}\,\sum_{q\in D_2}\,w(p)\,w(q)\,{\rm exp}\left(-\frac{\|p-q\|_2^2}{2\sigma^2}\right)\f$, + * + * or a second Gaussian kernel with bandwidth parameter \f$\tau >0\f$ is applied to their distance in this space + * (Gaussian Persistence Weighted Gaussian Kernel): + * + * \f$ GPWGK(D_1,D_2)={\rm exp}\left(-\frac{\|\Phi(D_1)-\Phi(D_2)\|^2}{2\tau^2} \right)\f$, + * where \f$\|\Phi(D_1)-\Phi(D_2)\|^2 = \langle\Phi(D_1)-\Phi(D_2),\Phi(D_1)-\Phi(D_2)\rangle\f$. + * + * It follows that the computation time is \f$O(n^2)\f$ where \f$n\f$ is the number of points + * in the diagrams. This time can be improved by computing approximations of the kernel + * with \f$m\f$ Fourier features \cite Rahimi07randomfeatures. In that case, the computation time becomes \f$O(mn)\f$. + * + * The PSSK is a Linear Persistence Weighted Gaussian Kernel between modified diagrams: + * the symmetric of each point with respect to the diagonal is first added in each diagram, and then the weight function + * is set to be +1 if the point is above the diagonal and -1 otherwise. + * + * \section sw Sliced Wasserstein Kernel + * + * The Sliced Wasserstein Kernel is defined as a Gaussian-like Kernel between persistence diagrams, where the distance used for + * comparison is the Sliced Wasserstein distance \f$SW\f$ between persistence diagrams, defined as the integral of the 1-norm + * between the sorted projections of the diagrams onto all lines passing through the origin: + * + * \f$ SW(D_1,D_2)=\int_{\theta\in\mathbb{S}}\,\|\pi_\theta(D_1\cup\pi_\Delta(D_2))-\pi_\theta(D_2\cup\pi_\Delta(D_1))\|_1{\rm d}\theta\f$, + * + * where \f$\pi_\theta\f$ is the projection onto the line defined with angle \f$\theta\f$ in the unit circle \f$\mathbb{S}\f$, + * and \f$\pi_\Delta\f$ is the projection onto the diagonal. + * The integral can be either computed exactly in \f$O(n^2{\rm log}(n))\f$ time, where \f$n\f$ is the number of points + * in the diagrams, or approximated by sampling \f$m\f$ lines in the circle in \f$O(mn{\rm log}(n))\f$ time. The SWK is then computed as: + * + * \f$ SWK(D_1,D_2) = {\rm exp}\left(-\frac{SW(D_1,D_2)}{2\sigma^2}\right).\f$ * * When launching: * - * \code $> ./BasicEx + * \code $> ./BasicEx ../../../../data/persistence_diagram/PD1 ../../../../data/persistence_diagram/PD2 * \endcode * * the program output is: * + * \include Kernels/kernel.txt + * * * \copyright GNU General Public License v3. * \verbatim Contact: gudhi-users@lists.gforge.inria.fr \endverbatim diff --git a/src/Kernels/example/kernel.txt b/src/Kernels/example/kernel.txt new file mode 100644 index 00000000..5fb8b504 --- /dev/null +++ b/src/Kernels/example/kernel.txt @@ -0,0 +1,8 @@ +SWK exact = 0.875446 +SWK approx = 0.875204 +PSSK exact = 0.0218669 +PSSK approx = 0.0213766 +LPWGK exact = 2.57351 +LPWGK approx = 2.49102 +GPWGK exact = 0.98783 +GPWGK approx = 0.987591 \ No newline at end of file diff --git a/src/Kernels/example/kernel_basic_example.cpp b/src/Kernels/example/kernel_basic_example.cpp index 8e9925c5..46e42c9d 100644 --- a/src/Kernels/example/kernel_basic_example.cpp +++ b/src/Kernels/example/kernel_basic_example.cpp @@ -20,29 +20,41 @@ * along with this program. If not, see . */ -#define NUMPI 3.14159265359 #include -int main() { +void usage(int nbArgs, char *const progName) { + std::cerr << "Error: Number of arguments (" << nbArgs << ") is not correct\n"; + std::cerr << "Usage: " << progName << " PD1 PD2 \n"; + std::cerr << " i.e.: " << progName << " ../../../../data/persistence_diagram/PD1 ../../../../data/persistence_diagram/PD2 \n"; + exit(-1); // ----- >> +} + +int main(int argc, char **argv) { - std::vector< std::pair > v1, v2; + if (argc != 3) usage(argc, argv[0]); double sigma = 2; double tau = 5; - v1.emplace_back(2.7, 3.7); - v1.emplace_back(9.6, 14.); - v1.emplace_back(34.2, 34.974); - - v2.emplace_back(2.8, 4.45); - v2.emplace_back(9.5, 14.1); - - std::cout << "SW exact = " << Gudhi::kernel::sw (v1, v2) << std::endl; - std::cout << "SW approx = " << Gudhi::kernel::approx_sw (v1, v2) << std::endl; - std::cout << "PSS exact = " << Gudhi::kernel::pss (v1,v2,sigma) << std::endl; - std::cout << "PSS approx = " << Gudhi::kernel::approx_pss (v1,v2,sigma) << std::endl; - std::cout << "PWG exact = " << Gudhi::kernel::lpwg (v1,v2,sigma) << std::endl; - std::cout << "PWG approx = " << Gudhi::kernel::approx_lpwg (v1,v2,sigma) << std::endl; - std::cout << "GPWG exact = " << Gudhi::kernel::gpwg (v1,v2,sigma,tau) << std::endl; - std::cout << "GPWG approx = " << Gudhi::kernel::approx_gpwg (v1,v2,sigma,tau) << std::endl; + std::string PDname1(argv[1]); std::string PDname2(argv[2]); + std::vector< std::pair > v1, v2; std::string line; double b,d; + + std::ifstream input1(PDname1); + while(std::getline(input1,line)){ + std::stringstream stream(line); stream >> b; stream >> d; v1.push_back(std::pair(b,d)); + } + + std::ifstream input2(PDname2); + while(std::getline(input2,line)){ + std::stringstream stream(line); stream >> b; stream >> d; v2.push_back(std::pair(b,d)); + } + + std::cout << "SWK exact = " << Gudhi::kernel::swk (v1,v2,sigma) << std::endl; + std::cout << "SWK approx = " << Gudhi::kernel::approx_swk (v1,v2,sigma) << std::endl; + std::cout << "PSSK exact = " << Gudhi::kernel::pssk (v1,v2,sigma) << std::endl; + std::cout << "PSSK approx = " << Gudhi::kernel::approx_pssk (v1,v2,sigma) << std::endl; + std::cout << "LPWGK exact = " << Gudhi::kernel::lpwgk (v1,v2,sigma) << std::endl; + std::cout << "LPWGK approx = " << Gudhi::kernel::approx_lpwgk (v1,v2,sigma) << std::endl; + std::cout << "GPWGK exact = " << Gudhi::kernel::gpwgk (v1,v2,sigma,tau) << std::endl; + std::cout << "GPWGK approx = " << Gudhi::kernel::approx_gpwgk (v1,v2,sigma,tau) << std::endl; } diff --git a/src/Kernels/include/gudhi/kernel.h b/src/Kernels/include/gudhi/kernel.h index 6429efed..44d984bd 100644 --- a/src/Kernels/include/gudhi/kernel.h +++ b/src/Kernels/include/gudhi/kernel.h @@ -23,8 +23,6 @@ #ifndef KERNEL_H_ #define KERNEL_H_ -#define NUMPI 3.14159265359 - #include #include #include @@ -80,7 +78,7 @@ double pss_weight(std::pair P){ * @param[in] weight weight function for the points in the diagrams. * */ -double lpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ +double lpwgk(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ int num_pts1 = PD1.size(); int num_pts2 = PD2.size(); double k = 0; for(int i = 0; i < num_pts1; i++) for(int j = 0; j < num_pts2; j++) @@ -96,10 +94,10 @@ double lpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair(PD1[i].second,PD1[i].first)); PD pd2 = PD2; numpts = PD2.size(); for(int i = 0; i < numpts; i++) pd2.push_back(std::pair(PD2[i].second,PD2[i].first)); - return lpwg(pd1, pd2, 2*sqrt(sigma), &pss_weight) / (2*8*NUMPI*sigma); + return lpwgk(pd1, pd2, 2*sqrt(sigma), &pss_weight) / (2*8*3.14159265359*sigma); } /** \brief Computes the Gaussian Persistence Weighted Gaussian Kernel between two persistence diagrams. @@ -112,10 +110,10 @@ double pss(PD PD1, PD PD2, double sigma){ * @param[in] weight weight function for the points in the diagrams. * */ -double gpwg(PD PD1, PD PD2, double sigma, double tau, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ - double k1 = lpwg(PD1,PD1,sigma,weight); - double k2 = lpwg(PD2,PD2,sigma,weight); - double k3 = lpwg(PD1,PD2,sigma,weight); +double gpwgk(PD PD1, PD PD2, double sigma, double tau, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ + double k1 = lpwgk(PD1,PD1,sigma,weight); + double k2 = lpwgk(PD2,PD2,sigma,weight); + double k3 = lpwgk(PD1,PD2,sigma,weight); return exp( - (k1+k2-2*k3) / (2*pow(tau,2)) ); } @@ -129,9 +127,9 @@ double gpwg(PD PD1, PD PD2, double sigma, double tau, double (*weight)(std::pair * */ double dpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ - double k1 = lpwg(PD1,PD1,sigma,weight); - double k2 = lpwg(PD2,PD2,sigma,weight); - double k3 = lpwg(PD1,PD2,sigma,weight); + double k1 = lpwgk(PD1,PD1,sigma,weight); + double k2 = lpwgk(PD2,PD2,sigma,weight); + double k3 = lpwgk(PD1,PD2,sigma,weight); return std::sqrt(k1+k2-2*k3); } @@ -160,24 +158,24 @@ double compute_angle(const PD & PersDiag, const int & i, const int & j){ // Valid only if alpha is in [-pi,pi] and beta-alpha is in [0,pi] double compute_int_cos(const double & alpha, const double & beta){ double res = 0; - assert((alpha >= 0 && alpha <= NUMPI) || (alpha >= -NUMPI && alpha <= 0)); - if (alpha >= 0 && alpha <= NUMPI){ + assert((alpha >= 0 && alpha <= 3.14159265359) || (alpha >= -3.14159265359 && alpha <= 0)); + if (alpha >= 0 && alpha <= 3.14159265359){ if (cos(alpha) >= 0){ - if(NUMPI/2 <= beta){res = 2-sin(alpha)-sin(beta);} + if(3.14159265359/2 <= beta){res = 2-sin(alpha)-sin(beta);} else{res = sin(beta)-sin(alpha);} } else{ - if(1.5*NUMPI <= beta){res = 2+sin(alpha)+sin(beta);} + if(1.5*3.14159265359 <= beta){res = 2+sin(alpha)+sin(beta);} else{res = sin(alpha)-sin(beta);} } } - if (alpha >= -NUMPI && alpha <= 0){ + if (alpha >= -3.14159265359 && alpha <= 0){ if (cos(alpha) <= 0){ - if(-NUMPI/2 <= beta){res = 2+sin(alpha)+sin(beta);} + if(-3.14159265359/2 <= beta){res = 2+sin(alpha)+sin(beta);} else{res = sin(alpha)-sin(beta);} } else{ - if(NUMPI/2 <= beta){res = 2-sin(alpha)-sin(beta);} + if(3.14159265359/2 <= beta){res = 2-sin(alpha)-sin(beta);} else{res = sin(beta)-sin(alpha);} } } @@ -202,9 +200,9 @@ double compute_sw(const std::vector > > & V1, int N = V1.size(); double sw = 0; for (int i = 0; i < N; i++){ std::vector > U,V; U = V1[i]; V = V2[i]; - double theta1, theta2; theta1 = -NUMPI/2; + double theta1, theta2; theta1 = -3.14159265359/2; unsigned int ku, kv; ku = 0; kv = 0; theta2 = std::min(U[ku].second,V[kv].second); - while(theta1 != NUMPI/2){ + while(theta1 != 3.14159265359/2){ if(PD1[U[ku].first].first != PD2[V[kv].first].first || PD1[U[ku].first].second != PD2[V[kv].first].second) if(theta1 != theta2) sw += compute_int(theta1, theta2, U[ku].first, V[kv].first, PD1, PD2); @@ -214,7 +212,7 @@ double compute_sw(const std::vector > > & V1, theta2 = std::min(U[ku].second, V[kv].second); } } - return sw/NUMPI; + return sw/3.14159265359; } /** \brief Computes the Sliced Wasserstein distance between two persistence diagrams. @@ -292,8 +290,8 @@ double compute_sw(const std::vector > > & V1, } for (int i = 0; i < N; i++){ - anglePerm1[order1[i]].push_back(std::pair(i,NUMPI/2)); - anglePerm2[order2[i]].push_back(std::pair(i,NUMPI/2)); + anglePerm1[order1[i]].push_back(std::pair(i,3.14159265359/2)); + anglePerm2[order2[i]].push_back(std::pair(i,3.14159265359/2)); } // Compute the SW distance with the list of inversions. @@ -301,6 +299,17 @@ double compute_sw(const std::vector > > & V1, } + /** \brief Computes the Sliced Wasserstein Kernel between two persistence diagrams. + * \ingroup kernel + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * @param[in] sigma bandwidth parameter. + * + */ + double swk(PD PD1, PD PD2, double sigma){ + return exp( - sw(PD1,PD2) / (2*pow(sigma, 2)) ); + } @@ -370,7 +379,7 @@ std::vector > random_Fourier(double sigma, int M = 1000 * @param[in] M number of Fourier features. * */ -double approx_lpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}, int M = 1000){ +double approx_lpwgk(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}, int M = 1000){ std::vector > Z = random_Fourier(sigma, M); std::vector > B1 = Fourier_feat(PD1,Z,weight); std::vector > B2 = Fourier_feat(PD2,Z,weight); @@ -386,10 +395,10 @@ double approx_lpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair(PD1[i].second,PD1[i].first)); PD pd2 = PD2; numpts = PD2.size(); for(int i = 0; i < numpts; i++) pd2.push_back(std::pair(PD2[i].second,PD2[i].first)); - return approx_lpwg(pd1, pd2, 2*sqrt(sigma), &pss_weight, M) / (2*8*NUMPI*sigma); + return approx_lpwgk(pd1, pd2, 2*sqrt(sigma), &pss_weight, M) / (2*8*3.14159265359*sigma); } @@ -404,7 +413,7 @@ double approx_pss(PD PD1, PD PD2, double sigma, int M = 1000){ * @param[in] M number of Fourier features. * */ -double approx_gpwg(PD PD1, PD PD2, double sigma, double tau, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}, int M = 1000){ +double approx_gpwgk(PD PD1, PD PD2, double sigma, double tau, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}, int M = 1000){ std::vector > Z = random_Fourier(sigma, M); std::vector > B1 = Fourier_feat(PD1,Z,weight); std::vector > B2 = Fourier_feat(PD2,Z,weight); @@ -422,7 +431,7 @@ double approx_gpwg(PD PD1, PD PD2, double sigma, double tau, double (*weight)(st */ double approx_sw(PD PD1, PD PD2, int N = 100){ - double step = NUMPI/N; double sw = 0; + double step = 3.14159265359/N; double sw = 0; // Add projections onto diagonal. int n1, n2; n1 = PD1.size(); n2 = PD2.size(); @@ -437,14 +446,27 @@ double approx_sw(PD PD1, PD PD2, int N = 100){ for (int i = 0; i < N; i++){ std::vector > L1, L2; for (int j = 0; j < n; j++){ - L1.push_back( std::pair(j, PD1[j].first*cos(-NUMPI/2+i*step) + PD1[j].second*sin(-NUMPI/2+i*step)) ); - L2.push_back( std::pair(j, PD2[j].first*cos(-NUMPI/2+i*step) + PD2[j].second*sin(-NUMPI/2+i*step)) ); + L1.push_back( std::pair(j, PD1[j].first*cos(-3.14159265359/2+i*step) + PD1[j].second*sin(-3.14159265359/2+i*step)) ); + L2.push_back( std::pair(j, PD2[j].first*cos(-3.14159265359/2+i*step) + PD2[j].second*sin(-3.14159265359/2+i*step)) ); } std::sort(L1.begin(),L1.end(), myComp); std::sort(L2.begin(),L2.end(), myComp); double f = 0; for (int j = 0; j < n; j++) f += std::abs(L1[j].second - L2[j].second); sw += f*step; } - return sw/NUMPI; + return sw/3.14159265359; +} + +/** \brief Computes an approximation of the Sliced Wasserstein Kernel between two persistence diagrams. + * \ingroup kernel + * + * @param[in] PD1 first persistence diagram. + * @param[in] PD2 second persistence diagram. + * @param[in] sigma bandwidth parameter. + * @param[in] N number of points sampled on the circle. + * + */ +double approx_swk(PD PD1, PD PD2, double sigma, int N = 100){ + return exp( - approx_sw(PD1,PD2,N) / (2*pow(sigma,2))); } diff --git a/src/Kernels/test/CMakeLists.txt b/src/Kernels/test/CMakeLists.txt new file mode 100644 index 00000000..9dbb9ed4 --- /dev/null +++ b/src/Kernels/test/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.6) +project(kernel_tests) + +include(GUDHI_test_coverage) + +add_executable ( kernel_test_unit test_kernel.cpp ) +target_link_libraries(kernel_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) +if (TBB_FOUND) + target_link_libraries(kernel_test_unit ${TBB_LIBRARIES}) +endif() + +file(COPY data DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) + +gudhi_add_coverage_test(kernel_test_unit) diff --git a/src/Kernels/test/test_kernel.cpp b/src/Kernels/test/test_kernel.cpp new file mode 100644 index 00000000..db05fd28 --- /dev/null +++ b/src/Kernels/test/test_kernel.cpp @@ -0,0 +1,56 @@ +/* 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): Mathieu Carrière + * + * Copyright (C) 2017 INRIA + * + * 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 . + */ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE "kernel" + +#include +#include // float comparison +#include +#include +#include +#include // std::max +#include +#include +#include + +BOOST_AUTO_TEST_CASE(check_PSS) { + std::vector< std::pair > v1, v2; + v1.emplace_back(std::pair(0,1)); + v2.emplace_back(std::pair(0,2)); + BOOST_CHECK(std::abs(Gudhi::kernel::pssk(v1,v2,1) - Gudhi::kernel::approx_pssk(v1,v2,1)) <= 1e-1); +} + +BOOST_AUTO_TEST_CASE(check_PWG) { + std::vector< std::pair > v1, v2; + v1.emplace_back(std::pair(0,1)); + v2.emplace_back(std::pair(0,2)); + BOOST_CHECK(std::abs(Gudhi::kernel::lpwgk(v1,v2,1) - Gudhi::kernel::approx_lpwgk(v1,v2,1)) <= 1e-1); + BOOST_CHECK(std::abs(Gudhi::kernel::gpwgk(v1,v2,1,1) - Gudhi::kernel::approx_gpwgk(v1,v2,1,1)) <= 1e-1); +} + +BOOST_AUTO_TEST_CASE(check_SW) { + std::vector< std::pair > v1, v2; + v2.emplace_back(std::pair(0,2)); + BOOST_CHECK(std::abs(Gudhi::kernel::sw(v1,v2) - Gudhi::kernel::approx_sw(v1,v2)) <= 1e-3); + BOOST_CHECK(std::abs(Gudhi::kernel::sw(v1,v2) - 2*std::sqrt(2)/3.1415) <= 1e-3); +} -- cgit v1.2.3 -- cgit v1.2.3 From b39719177ee05b91be9abdafe093591fae2040e9 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Tue, 9 Jan 2018 16:25:48 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3121 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: d7b24f452fd8e75aac61c40f0281dca683d7fa03 --- src/Kernels/example/CMakeLists.txt | 11 ----------- src/Kernels/test/CMakeLists.txt | 2 -- 2 files changed, 13 deletions(-) delete mode 100644 src/Kernels/example/CMakeLists.txt diff --git a/src/Kernels/example/CMakeLists.txt b/src/Kernels/example/CMakeLists.txt deleted file mode 100644 index 57e13004..00000000 --- a/src/Kernels/example/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 2.6) -project(Kernels_examples) - -add_executable ( BasicEx kernel_basic_example.cpp ) - -if (TBB_FOUND) - target_link_libraries(BasicEx ${TBB_LIBRARIES}) -endif() - -add_test(NAME Kernels_example_basicex COMMAND $ - "") diff --git a/src/Kernels/test/CMakeLists.txt b/src/Kernels/test/CMakeLists.txt index 9dbb9ed4..95c72a7f 100644 --- a/src/Kernels/test/CMakeLists.txt +++ b/src/Kernels/test/CMakeLists.txt @@ -9,6 +9,4 @@ if (TBB_FOUND) target_link_libraries(kernel_test_unit ${TBB_LIBRARIES}) endif() -file(COPY data DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) - gudhi_add_coverage_test(kernel_test_unit) -- cgit v1.2.3 From 73392dcec4e9977ca862eaf1cf9beaa6f9a133f7 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Tue, 9 Jan 2018 16:27:19 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3122 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: e47b68d4c035119352ad433a9efd516aef3a70f5 --- src/Kernels/example/CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/Kernels/example/CMakeLists.txt diff --git a/src/Kernels/example/CMakeLists.txt b/src/Kernels/example/CMakeLists.txt new file mode 100644 index 00000000..d8ad4b42 --- /dev/null +++ b/src/Kernels/example/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.6) +project(Kernels_examples) + +add_executable ( BasicEx kernel_basic_example.cpp ) + +if (TBB_FOUND) + target_link_libraries(BasicEx ${TBB_LIBRARIES}) +endif() + +add_test(NAME Kernels_example_basicex COMMAND $ "${CMAKE_SOURCE_DIR}/data/persistence_diagram/PD1" "${CMAKE_SOURCE_DIR}/data/persistence_diagram/PD2") \ No newline at end of file -- cgit v1.2.3 From 1c6a680aee1ff1193ea546cfaeb63b18d38b97fa Mon Sep 17 00:00:00 2001 From: mcarrier Date: Thu, 11 Jan 2018 11:04:38 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3125 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 4064ad30936118d97d16f84a5288e349f86109b6 --- src/Kernels/include/gudhi/kernel.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Kernels/include/gudhi/kernel.h b/src/Kernels/include/gudhi/kernel.h index 44d984bd..30864476 100644 --- a/src/Kernels/include/gudhi/kernel.h +++ b/src/Kernels/include/gudhi/kernel.h @@ -43,8 +43,8 @@ #include #include #include -#include -#include +//#include +//#include #include #include #include -- cgit v1.2.3 From be131d6f74a9264e15a0b1c1e72fa8967c4518bd Mon Sep 17 00:00:00 2001 From: mcarrier Date: Thu, 11 Jan 2018 16:31:30 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3129 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 567185cf3691252c2685c450d65787d824442bb2 --- src/Kernels/example/kernel_basic_example.cpp | 5 ++ src/Kernels/include/gudhi/kernel.h | 95 +++++++++++++--------------- 2 files changed, 49 insertions(+), 51 deletions(-) diff --git a/src/Kernels/example/kernel_basic_example.cpp b/src/Kernels/example/kernel_basic_example.cpp index 46e42c9d..85ce36d4 100644 --- a/src/Kernels/example/kernel_basic_example.cpp +++ b/src/Kernels/example/kernel_basic_example.cpp @@ -21,6 +21,11 @@ */ #include +#include +#include +#include +#include + void usage(int nbArgs, char *const progName) { std::cerr << "Error: Number of arguments (" << nbArgs << ") is not correct\n"; diff --git a/src/Kernels/include/gudhi/kernel.h b/src/Kernels/include/gudhi/kernel.h index 30864476..900db092 100644 --- a/src/Kernels/include/gudhi/kernel.h +++ b/src/Kernels/include/gudhi/kernel.h @@ -4,7 +4,7 @@ * * Author(s): Mathieu Carrière * - * Copyright (C) 2017 INRIA (France) + * Copyright (C) 2018 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 @@ -23,45 +23,32 @@ #ifndef KERNEL_H_ #define KERNEL_H_ -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include #include -#include -#include -#include -#include -//#include -//#include #include -#include -#include +#include -using PD = std::vector >; -bool sortAngle(const std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);} -bool myComp(const std::pair & P1, const std::pair & P2){return P1.second < P2.second;} namespace Gudhi { namespace kernel { +using PD = std::vector >; +double pi = boost::math::constants::pi(); + +bool sortAngle(const std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);} +bool myComp(const std::pair & P1, const std::pair & P2){return P1.second < P2.second;} double pss_weight(std::pair P){ if(P.second > P.first) return 1; else return -1; } +double arctan_weight(std::pair P){ + return atan(P.second - P.first); +} + @@ -78,7 +65,8 @@ double pss_weight(std::pair P){ * @param[in] weight weight function for the points in the diagrams. * */ -double lpwgk(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ +template) > +double lpwgk(const PD & PD1, const PD & PD2, double sigma, Weight weight = arctan_weight){ int num_pts1 = PD1.size(); int num_pts2 = PD2.size(); double k = 0; for(int i = 0; i < num_pts1; i++) for(int j = 0; j < num_pts2; j++) @@ -94,10 +82,10 @@ double lpwgk(PD PD1, PD PD2, double sigma, double (*weight)(std::pair(PD1[i].second,PD1[i].first)); PD pd2 = PD2; numpts = PD2.size(); for(int i = 0; i < numpts; i++) pd2.push_back(std::pair(PD2[i].second,PD2[i].first)); - return lpwgk(pd1, pd2, 2*sqrt(sigma), &pss_weight) / (2*8*3.14159265359*sigma); + return lpwgk(pd1, pd2, 2*sqrt(sigma), &pss_weight) / (2*8*pi*sigma); } /** \brief Computes the Gaussian Persistence Weighted Gaussian Kernel between two persistence diagrams. @@ -110,7 +98,8 @@ double pssk(PD PD1, PD PD2, double sigma){ * @param[in] weight weight function for the points in the diagrams. * */ -double gpwgk(PD PD1, PD PD2, double sigma, double tau, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ +template) > +double gpwgk(const PD & PD1, const PD & PD2, double sigma, double tau, Weight weight = arctan_weight){ double k1 = lpwgk(PD1,PD1,sigma,weight); double k2 = lpwgk(PD2,PD2,sigma,weight); double k3 = lpwgk(PD1,PD2,sigma,weight); @@ -126,7 +115,8 @@ double gpwgk(PD PD1, PD PD2, double sigma, double tau, double (*weight)(std::pai * @param[in] weight weight function for the points in the diagrams. * */ -double dpwg(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ +template) > +double dpwg(const PD & PD1, const PD & PD2, double sigma, Weight weight = arctan_weight){ double k1 = lpwgk(PD1,PD1,sigma,weight); double k2 = lpwgk(PD2,PD2,sigma,weight); double k3 = lpwgk(PD1,PD2,sigma,weight); @@ -158,24 +148,24 @@ double compute_angle(const PD & PersDiag, const int & i, const int & j){ // Valid only if alpha is in [-pi,pi] and beta-alpha is in [0,pi] double compute_int_cos(const double & alpha, const double & beta){ double res = 0; - assert((alpha >= 0 && alpha <= 3.14159265359) || (alpha >= -3.14159265359 && alpha <= 0)); - if (alpha >= 0 && alpha <= 3.14159265359){ + //assert((alpha >= 0 && alpha <= pi) || (alpha >= -pi && alpha <= 0)); + if (alpha >= 0 && alpha <= pi){ if (cos(alpha) >= 0){ - if(3.14159265359/2 <= beta){res = 2-sin(alpha)-sin(beta);} + if(pi/2 <= beta){res = 2-sin(alpha)-sin(beta);} else{res = sin(beta)-sin(alpha);} } else{ - if(1.5*3.14159265359 <= beta){res = 2+sin(alpha)+sin(beta);} + if(1.5*pi <= beta){res = 2+sin(alpha)+sin(beta);} else{res = sin(alpha)-sin(beta);} } } - if (alpha >= -3.14159265359 && alpha <= 0){ + if (alpha >= -pi && alpha <= 0){ if (cos(alpha) <= 0){ - if(-3.14159265359/2 <= beta){res = 2+sin(alpha)+sin(beta);} + if(-pi/2 <= beta){res = 2+sin(alpha)+sin(beta);} else{res = sin(alpha)-sin(beta);} } else{ - if(3.14159265359/2 <= beta){res = 2-sin(alpha)-sin(beta);} + if(pi/2 <= beta){res = 2-sin(alpha)-sin(beta);} else{res = sin(beta)-sin(alpha);} } } @@ -200,9 +190,9 @@ double compute_sw(const std::vector > > & V1, int N = V1.size(); double sw = 0; for (int i = 0; i < N; i++){ std::vector > U,V; U = V1[i]; V = V2[i]; - double theta1, theta2; theta1 = -3.14159265359/2; + double theta1, theta2; theta1 = -pi/2; unsigned int ku, kv; ku = 0; kv = 0; theta2 = std::min(U[ku].second,V[kv].second); - while(theta1 != 3.14159265359/2){ + while(theta1 != pi/2){ if(PD1[U[ku].first].first != PD2[V[kv].first].first || PD1[U[ku].first].second != PD2[V[kv].first].second) if(theta1 != theta2) sw += compute_int(theta1, theta2, U[ku].first, V[kv].first, PD1, PD2); @@ -212,7 +202,7 @@ double compute_sw(const std::vector > > & V1, theta2 = std::min(U[ku].second, V[kv].second); } } - return sw/3.14159265359; + return sw/pi; } /** \brief Computes the Sliced Wasserstein distance between two persistence diagrams. @@ -234,7 +224,7 @@ double compute_sw(const std::vector > > & V1, max_ordinate = std::max(max_ordinate, PD1[i].second); PD2.push_back( std::pair( ((PD1[i].first+PD1[i].second)/2), ((PD1[i].first+PD1[i].second)/2) ) ); } - int N = PD1.size(); assert(N==PD2.size()); + int N = PD1.size(); // Slightly perturb the points so that the PDs are in generic positions. int mag = 0; while(max_ordinate > 10){mag++; max_ordinate/=10;} @@ -290,8 +280,8 @@ double compute_sw(const std::vector > > & V1, } for (int i = 0; i < N; i++){ - anglePerm1[order1[i]].push_back(std::pair(i,3.14159265359/2)); - anglePerm2[order2[i]].push_back(std::pair(i,3.14159265359/2)); + anglePerm1[order1[i]].push_back(std::pair(i,pi/2)); + anglePerm2[order2[i]].push_back(std::pair(i,pi/2)); } // Compute the SW distance with the list of inversions. @@ -344,7 +334,8 @@ double approx_dpwg_Fourier(const std::vector >& B1, con return std::sqrt((1.0/M)*(d1+d2)-2*d3); } -std::vector > Fourier_feat(PD D, std::vector > Z, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}){ +template) > +std::vector > Fourier_feat(PD D, std::vector > Z, Weight weight = arctan_weight){ int m = D.size(); std::vector > B; int M = Z.size(); for(int i = 0; i < M; i++){ double d1 = 0; double d2 = 0; double zx = Z[i].first; double zy = Z[i].second; @@ -379,7 +370,8 @@ std::vector > random_Fourier(double sigma, int M = 1000 * @param[in] M number of Fourier features. * */ -double approx_lpwgk(PD PD1, PD PD2, double sigma, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}, int M = 1000){ +template) > +double approx_lpwgk(const PD & PD1, const PD & PD2, double sigma, Weight weight = arctan_weight, int M = 1000){ std::vector > Z = random_Fourier(sigma, M); std::vector > B1 = Fourier_feat(PD1,Z,weight); std::vector > B2 = Fourier_feat(PD2,Z,weight); @@ -395,10 +387,10 @@ double approx_lpwgk(PD PD1, PD PD2, double sigma, double (*weight)(std::pair(PD1[i].second,PD1[i].first)); PD pd2 = PD2; numpts = PD2.size(); for(int i = 0; i < numpts; i++) pd2.push_back(std::pair(PD2[i].second,PD2[i].first)); - return approx_lpwgk(pd1, pd2, 2*sqrt(sigma), &pss_weight, M) / (2*8*3.14159265359*sigma); + return approx_lpwgk(pd1, pd2, 2*sqrt(sigma), &pss_weight, M) / (2*8*pi*sigma); } @@ -413,7 +405,8 @@ double approx_pssk(PD PD1, PD PD2, double sigma, int M = 1000){ * @param[in] M number of Fourier features. * */ -double approx_gpwgk(PD PD1, PD PD2, double sigma, double tau, double (*weight)(std::pair) = [](std::pair P){return atan(P.second - P.first);}, int M = 1000){ +template) > +double approx_gpwgk(const PD & PD1, const PD & PD2, double sigma, double tau, Weight weight = arctan_weight, int M = 1000){ std::vector > Z = random_Fourier(sigma, M); std::vector > B1 = Fourier_feat(PD1,Z,weight); std::vector > B2 = Fourier_feat(PD2,Z,weight); @@ -431,7 +424,7 @@ double approx_gpwgk(PD PD1, PD PD2, double sigma, double tau, double (*weight)(s */ double approx_sw(PD PD1, PD PD2, int N = 100){ - double step = 3.14159265359/N; double sw = 0; + double step = pi/N; double sw = 0; // Add projections onto diagonal. int n1, n2; n1 = PD1.size(); n2 = PD2.size(); @@ -446,14 +439,14 @@ double approx_sw(PD PD1, PD PD2, int N = 100){ for (int i = 0; i < N; i++){ std::vector > L1, L2; for (int j = 0; j < n; j++){ - L1.push_back( std::pair(j, PD1[j].first*cos(-3.14159265359/2+i*step) + PD1[j].second*sin(-3.14159265359/2+i*step)) ); - L2.push_back( std::pair(j, PD2[j].first*cos(-3.14159265359/2+i*step) + PD2[j].second*sin(-3.14159265359/2+i*step)) ); + L1.push_back( std::pair(j, PD1[j].first*cos(-pi/2+i*step) + PD1[j].second*sin(-pi/2+i*step)) ); + L2.push_back( std::pair(j, PD2[j].first*cos(-pi/2+i*step) + PD2[j].second*sin(-pi/2+i*step)) ); } std::sort(L1.begin(),L1.end(), myComp); std::sort(L2.begin(),L2.end(), myComp); double f = 0; for (int j = 0; j < n; j++) f += std::abs(L1[j].second - L2[j].second); sw += f*step; } - return sw/3.14159265359; + return sw/pi; } /** \brief Computes an approximation of the Sliced Wasserstein Kernel between two persistence diagrams. -- cgit v1.2.3 From ff0dc023588e3b33bc4bc7f26ce1f68c647ae441 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Fri, 16 Feb 2018 15:43:29 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3251 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 80f084fc990df6e5c6b60ac83514220aba2ceb5c --- .../example/CMakeLists.txt | 10 +++ .../example/persistence_weighted_gaussian.cpp | 34 +++++----- .../example/sliced_wasserstein.cpp | 16 +++-- .../include/gudhi/Persistence_weighted_gaussian.h | 78 ++++++++++++---------- .../include/gudhi/Sliced_Wasserstein.h | 26 +++++--- 5 files changed, 98 insertions(+), 66 deletions(-) diff --git a/src/Persistence_representations/example/CMakeLists.txt b/src/Persistence_representations/example/CMakeLists.txt index 54d719ac..79d39c4d 100644 --- a/src/Persistence_representations/example/CMakeLists.txt +++ b/src/Persistence_representations/example/CMakeLists.txt @@ -27,3 +27,13 @@ add_test(NAME Persistence_representations_example_heat_maps COMMAND $) install(TARGETS Persistence_representations_example_heat_maps DESTINATION bin) +add_executable ( Sliced_Wasserstein sliced_wasserstein.cpp ) +add_test(NAME Sliced_Wasserstein + COMMAND $) +install(TARGETS Sliced_Wasserstein DESTINATION bin) + +add_executable ( Persistence_weighted_gaussian persistence_weighted_gaussian.cpp ) +add_test(NAME Persistence_weighted_gaussian + COMMAND $) +install(TARGETS Persistence_weighted_gaussian DESTINATION bin) + diff --git a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp index e95b9445..a0e820ea 100644 --- a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp +++ b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp @@ -44,21 +44,24 @@ int main(int argc, char** argv) { persistence2.push_back(std::make_pair(3, 5)); persistence2.push_back(std::make_pair(6, 10)); - PWG PWG1(persistence1); - PWG PWG2(persistence2); double sigma = 1; double tau = 1; - int m = 1000; + int m = 10000; + PWG PWG1(persistence1, sigma, m, PWG::arctan_weight); + PWG PWG2(persistence2, sigma, m, PWG::arctan_weight); + + PWG PWGex1(persistence1, sigma, -1, PWG::arctan_weight); + PWG PWGex2(persistence2, sigma, -1, PWG::arctan_weight); // Linear PWG - std::cout << PWG1.compute_scalar_product (PWG2, sigma, PWG::arctan_weight, m) << std::endl; - std::cout << PWG1.compute_scalar_product (PWG2, sigma, PWG::arctan_weight, -1) << std::endl; + std::cout << PWG1.compute_scalar_product (PWG2) << std::endl; + std::cout << PWGex1.compute_scalar_product (PWGex2) << std::endl; - std::cout << PWG1.distance (PWG2, sigma, PWG::arctan_weight, m) << std::endl; - std::cout << PWG1.distance (PWG2, sigma, PWG::arctan_weight, -1) << std::endl; + std::cout << PWG1.distance (PWG2) << std::endl; + std::cout << PWGex1.distance (PWGex2) << std::endl; @@ -68,8 +71,8 @@ int main(int argc, char** argv) { // Gaussian PWG - std::cout << std::exp( -PWG1.distance (PWG2, sigma, PWG::arctan_weight, m, 2) ) / (2*tau*tau) << std::endl; - std::cout << std::exp( -PWG1.distance (PWG2, sigma, PWG::arctan_weight, -1, 2) ) / (2*tau*tau) << std::endl; + std::cout << std::exp( -PWG1.distance (PWG2, 2) ) / (2*tau*tau) << std::endl; + std::cout << std::exp( -PWGex1.distance (PWGex2, 2) ) / (2*tau*tau) << std::endl; @@ -82,14 +85,15 @@ int main(int argc, char** argv) { PD pd1 = persistence1; int numpts = persistence1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(persistence1[i].second,persistence1[i].first); PD pd2 = persistence2; numpts = persistence2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(persistence2[i].second,persistence2[i].first); - PWG pwg1(pd1); - PWG pwg2(pd2); + PWG pwg1(pd1, 2*std::sqrt(sigma), m, PWG::pss_weight); + PWG pwg2(pd2, 2*std::sqrt(sigma), m, PWG::pss_weight); + + PWG pwgex1(pd1, 2*std::sqrt(sigma), -1, PWG::pss_weight); + PWG pwgex2(pd2, 2*std::sqrt(sigma), -1, PWG::pss_weight); - std::cout << pwg1.compute_scalar_product (pwg2, 2*std::sqrt(sigma), PWG::pss_weight, m) / (16*pi*sigma) << std::endl; - std::cout << pwg1.compute_scalar_product (pwg2, 2*std::sqrt(sigma), PWG::pss_weight, -1) / (16*pi*sigma) << std::endl; + std::cout << pwg1.compute_scalar_product (pwg2) / (16*pi*sigma) << std::endl; + std::cout << pwgex1.compute_scalar_product (pwgex2) / (16*pi*sigma) << std::endl; - std::cout << pwg1.distance (pwg2, 2*std::sqrt(sigma), PWG::pss_weight, m) / (16*pi*sigma) << std::endl; - std::cout << pwg1.distance (pwg2, 2*std::sqrt(sigma), PWG::pss_weight, -1) / (16*pi*sigma) << std::endl; return 0; diff --git a/src/Persistence_representations/example/sliced_wasserstein.cpp b/src/Persistence_representations/example/sliced_wasserstein.cpp index 673d8474..f153fbe8 100644 --- a/src/Persistence_representations/example/sliced_wasserstein.cpp +++ b/src/Persistence_representations/example/sliced_wasserstein.cpp @@ -43,13 +43,17 @@ int main(int argc, char** argv) { persistence2.push_back(std::make_pair(3, 5)); persistence2.push_back(std::make_pair(6, 10)); - SW SW1(persistence1); - SW SW2(persistence2); - std::cout << SW1.compute_sliced_wasserstein_distance(SW2,100) << std::endl; - std::cout << SW1.compute_sliced_wasserstein_distance(SW2,-1) << std::endl; - std::cout << SW1.compute_scalar_product(SW2,1,100) << std::endl; - std::cout << SW1.distance(SW2,1,100,1) << std::endl; + SW sw1(persistence1, 1, 100); + SW sw2(persistence2, 1, 100); + + SW swex1(persistence1, 1, -1); + SW swex2(persistence2, 1, -1); + + std::cout << sw1.compute_sliced_wasserstein_distance(sw2) << std::endl; + std::cout << swex1.compute_sliced_wasserstein_distance(swex2) << std::endl; + std::cout << sw1.compute_scalar_product(sw2) << std::endl; + std::cout << swex1.distance(swex2) << std::endl; return 0; } diff --git a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h index 2884885c..2b25b9a8 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h +++ b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h @@ -45,6 +45,7 @@ double pi = boost::math::constants::pi(); using PD = std::vector >; +using Weight = std::function) >; namespace Gudhi { namespace Persistence_representations { @@ -53,11 +54,18 @@ class Persistence_weighted_gaussian{ protected: PD diagram; + Weight weight; + double sigma; + int approx; public: - Persistence_weighted_gaussian(PD _diagram){diagram = _diagram;} + Persistence_weighted_gaussian(PD _diagram){diagram = _diagram; sigma = 1.0; approx = 1000; weight = arctan_weight;} + Persistence_weighted_gaussian(PD _diagram, double _sigma, int _approx, Weight _weight){diagram = _diagram; sigma = _sigma; approx = _approx; weight = _weight;} PD get_diagram(){return this->diagram;} + double get_sigma(){return this->sigma;} + int get_approx(){return this->approx;} + Weight get_weight(){return this->weight;} // ********************************** @@ -65,38 +73,37 @@ class Persistence_weighted_gaussian{ // ********************************** - static double pss_weight(std::pair P){ - if(P.second > P.first) return 1; + static double pss_weight(std::pair p){ + if(p.second > p.first) return 1; else return -1; } - static double arctan_weight(std::pair P){ - return atan(P.second - P.first); + static double arctan_weight(std::pair p){ + return atan(p.second - p.first); } - template) > > - std::vector > Fourier_feat(PD D, std::vector > Z, Weight weight = arctan_weight){ - int m = D.size(); std::vector > B; int M = Z.size(); - for(int i = 0; i < M; i++){ - double d1 = 0; double d2 = 0; double zx = Z[i].first; double zy = Z[i].second; - for(int j = 0; j < m; j++){ - double x = D[j].first; double y = D[j].second; - d1 += weight(D[j])*cos(x*zx + y*zy); - d2 += weight(D[j])*sin(x*zx + y*zy); + std::vector > Fourier_feat(PD diag, std::vector > z, Weight weight = arctan_weight){ + int md = diag.size(); std::vector > b; int mz = z.size(); + for(int i = 0; i < mz; i++){ + double d1 = 0; double d2 = 0; double zx = z[i].first; double zy = z[i].second; + for(int j = 0; j < md; j++){ + double x = diag[j].first; double y = diag[j].second; + d1 += weight(diag[j])*cos(x*zx + y*zy); + d2 += weight(diag[j])*sin(x*zx + y*zy); } - B.emplace_back(d1,d2); + b.emplace_back(d1,d2); } - return B; + return b; } - std::vector > random_Fourier(double sigma, int M = 1000){ - std::normal_distribution distrib(0,1); std::vector > Z; std::random_device rd; - for(int i = 0; i < M; i++){ + std::vector > random_Fourier(double sigma, int m = 1000){ + std::normal_distribution distrib(0,1); std::vector > z; std::random_device rd; + for(int i = 0; i < m; i++){ std::mt19937 e1(rd()); std::mt19937 e2(rd()); double zx = distrib(e1); double zy = distrib(e2); - Z.emplace_back(zx/sigma,zy/sigma); + z.emplace_back(zx/sigma,zy/sigma); } - return Z; + return z; } @@ -106,32 +113,33 @@ class Persistence_weighted_gaussian{ // ********************************** - template) > > - double compute_scalar_product(Persistence_weighted_gaussian second, double sigma, Weight weight = arctan_weight, int m = 1000){ + double compute_scalar_product(Persistence_weighted_gaussian second){ PD diagram1 = this->diagram; PD diagram2 = second.diagram; - if(m == -1){ + if(this->approx == -1){ int num_pts1 = diagram1.size(); int num_pts2 = diagram2.size(); double k = 0; for(int i = 0; i < num_pts1; i++) for(int j = 0; j < num_pts2; j++) - k += weight(diagram1[i])*weight(diagram2[j])*exp(-((diagram1[i].first - diagram2[j].first) * (diagram1[i].first - diagram2[j].first) + - (diagram1[i].second - diagram2[j].second) * (diagram1[i].second - diagram2[j].second)) - /(2*sigma*sigma)); + k += this->weight(diagram1[i])*this->weight(diagram2[j])*exp(-((diagram1[i].first - diagram2[j].first) * (diagram1[i].first - diagram2[j].first) + + (diagram1[i].second - diagram2[j].second) * (diagram1[i].second - diagram2[j].second)) + /(2*this->sigma*this->sigma)); return k; } else{ - std::vector > z = random_Fourier(sigma, m); - std::vector > b1 = Fourier_feat(diagram1,z,weight); - std::vector > b2 = Fourier_feat(diagram2,z,weight); - double d = 0; for(int i = 0; i < m; i++) d += b1[i].first*b2[i].first + b1[i].second*b2[i].second; - return d/m; + std::vector > z = random_Fourier(this->sigma, this->approx); + std::vector > b1 = Fourier_feat(diagram1,z,this->weight); + std::vector > b2 = Fourier_feat(diagram2,z,this->weight); + double d = 0; for(int i = 0; i < this->approx; i++) d += b1[i].first*b2[i].first + b1[i].second*b2[i].second; + return d/this->approx; } } - template) > > - double distance(Persistence_weighted_gaussian second, double sigma, Weight weight = arctan_weight, int m = 1000, double power = 1) { - return std::pow(this->compute_scalar_product(*this, sigma, weight, m) + second.compute_scalar_product(second, sigma, weight, m)-2*this->compute_scalar_product(second, sigma, weight, m), power/2.0); + double distance(Persistence_weighted_gaussian second, double power = 1) { + if(this->sigma != second.get_sigma() || this->approx != second.get_approx()){ + std::cout << "Error: different representations!" << std::endl; return 0; + } + else return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), power/2.0); } diff --git a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h index 4fa6151f..ad1a6c42 100644 --- a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h +++ b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h @@ -53,11 +53,16 @@ class Sliced_Wasserstein { protected: PD diagram; + int approx; + double sigma; public: - Sliced_Wasserstein(PD _diagram){diagram = _diagram;} + Sliced_Wasserstein(PD _diagram){diagram = _diagram; approx = 100; sigma = 0.001;} + Sliced_Wasserstein(PD _diagram, double _sigma, int _approx){diagram = _diagram; approx = _approx; sigma = _sigma;} PD get_diagram(){return this->diagram;} + int get_approx(){return this->approx;} + double get_sigma(){return this->sigma;} // ********************************** @@ -130,11 +135,11 @@ class Sliced_Wasserstein { // Scalar product + distance. // ********************************** - double compute_sliced_wasserstein_distance(Sliced_Wasserstein second, int approx) { + double compute_sliced_wasserstein_distance(Sliced_Wasserstein second) { PD diagram1 = this->diagram; PD diagram2 = second.diagram; double sw = 0; - if(approx == -1){ + if(this->approx == -1){ // Add projections onto diagonal. int n1, n2; n1 = diagram1.size(); n2 = diagram2.size(); double max_ordinate = std::numeric_limits::lowest(); @@ -226,7 +231,7 @@ class Sliced_Wasserstein { else{ - double step = pi/approx; + double step = pi/this->approx; // Add projections onto diagonal. int n1, n2; n1 = diagram1.size(); n2 = diagram2.size(); @@ -238,7 +243,7 @@ class Sliced_Wasserstein { // Sort and compare all projections. #ifdef GUDHI_USE_TBB - tbb::parallel_for(0, approx, [&](int i){ + tbb::parallel_for(0, this->approx, [&](int i){ std::vector > l1, l2; for (int j = 0; j < n; j++){ l1.emplace_back( j, diagram1[j].first*cos(-pi/2+i*step) + diagram1[j].second*sin(-pi/2+i*step) ); @@ -250,7 +255,7 @@ class Sliced_Wasserstein { sw += f*step; }); #else - for (int i = 0; i < approx; i++){ + for (int i = 0; i < this->approx; i++){ std::vector > l1, l2; for (int j = 0; j < n; j++){ l1.emplace_back( j, diagram1[j].first*cos(-pi/2+i*step) + diagram1[j].second*sin(-pi/2+i*step) ); @@ -268,12 +273,13 @@ class Sliced_Wasserstein { } - double compute_scalar_product(Sliced_Wasserstein second, double sigma, int approx = 100) { - return std::exp(-compute_sliced_wasserstein_distance(second, approx)/(2*sigma*sigma)); + double compute_scalar_product(Sliced_Wasserstein second){ + return std::exp(-compute_sliced_wasserstein_distance(second)/(2*this->sigma*this->sigma)); } - double distance(Sliced_Wasserstein second, double sigma, int approx = 100, double power = 1) { - return std::pow(this->compute_scalar_product(*this, sigma, approx) + second.compute_scalar_product(second, sigma, approx)-2*this->compute_scalar_product(second, sigma, approx), power/2.0); + double distance(Sliced_Wasserstein second, double power = 1) { + if(this->sigma != second.sigma || this->approx != second.approx){std::cout << "Error: different representations!" << std::endl; return 0;} + else return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), power/2.0); } -- cgit v1.2.3 From 220a91b55e0952947f96ea4a09085b0720466c64 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Tue, 27 Feb 2018 15:56:28 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3259 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: eedc1bf47f0194b23dfc02425bedd2682bd3f573 --- .../include/gudhi/Persistence_weighted_gaussian.h | 4 --- .../include/gudhi/Sliced_Wasserstein.h | 38 ++++++---------------- 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h index 2b25b9a8..a6efa72d 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h +++ b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h @@ -23,10 +23,6 @@ #ifndef PERSISTENCE_WEIGHTED_GAUSSIAN_H_ #define PERSISTENCE_WEIGHTED_GAUSSIAN_H_ -#ifdef GUDHI_USE_TBB -#include -#endif - // gudhi include #include diff --git a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h index ad1a6c42..6196e207 100644 --- a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h +++ b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h @@ -23,10 +23,6 @@ #ifndef SLICED_WASSERSTEIN_H_ #define SLICED_WASSERSTEIN_H_ -#ifdef GUDHI_USE_TBB -#include -#endif - // gudhi include #include @@ -242,31 +238,17 @@ class Sliced_Wasserstein { int n = diagram1.size(); // Sort and compare all projections. - #ifdef GUDHI_USE_TBB - tbb::parallel_for(0, this->approx, [&](int i){ - std::vector > l1, l2; - for (int j = 0; j < n; j++){ - l1.emplace_back( j, diagram1[j].first*cos(-pi/2+i*step) + diagram1[j].second*sin(-pi/2+i*step) ); - l2.emplace_back( j, diagram2[j].first*cos(-pi/2+i*step) + diagram2[j].second*sin(-pi/2+i*step) ); - } - std::sort(l1.begin(),l1.end(), [=](const std::pair & p1, const std::pair & p2){return p1.second < p2.second;}); - std::sort(l2.begin(),l2.end(), [=](const std::pair & p1, const std::pair & p2){return p1.second < p2.second;}); - double f = 0; for (int j = 0; j < n; j++) f += std::abs(l1[j].second - l2[j].second); - sw += f*step; - }); - #else - for (int i = 0; i < this->approx; i++){ - std::vector > l1, l2; - for (int j = 0; j < n; j++){ - l1.emplace_back( j, diagram1[j].first*cos(-pi/2+i*step) + diagram1[j].second*sin(-pi/2+i*step) ); - l2.emplace_back( j, diagram2[j].first*cos(-pi/2+i*step) + diagram2[j].second*sin(-pi/2+i*step) ); - } - std::sort(l1.begin(),l1.end(), [=](const std::pair & p1, const std::pair & p2){return p1.second < p2.second;}); - std::sort(l2.begin(),l2.end(), [=](const std::pair & p1, const std::pair & p2){return p1.second < p2.second;}); - double f = 0; for (int j = 0; j < n; j++) f += std::abs(l1[j].second - l2[j].second); - sw += f*step; + for (int i = 0; i < this->approx; i++){ + std::vector > l1, l2; + for (int j = 0; j < n; j++){ + l1.emplace_back( j, diagram1[j].first*cos(-pi/2+i*step) + diagram1[j].second*sin(-pi/2+i*step) ); + l2.emplace_back( j, diagram2[j].first*cos(-pi/2+i*step) + diagram2[j].second*sin(-pi/2+i*step) ); } - #endif + std::sort(l1.begin(),l1.end(), [=](const std::pair & p1, const std::pair & p2){return p1.second < p2.second;}); + std::sort(l2.begin(),l2.end(), [=](const std::pair & p1, const std::pair & p2){return p1.second < p2.second;}); + double f = 0; for (int j = 0; j < n; j++) f += std::abs(l1[j].second - l2[j].second); + sw += f*step; + } } return sw/pi; -- cgit v1.2.3 From d574f7f65acdd6dde92150879c06db5e6e0b75a9 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Mon, 5 Mar 2018 13:57:02 +0000 Subject: added files for cythonization of kernels git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3263 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 5bc7aadf2696ea2f94384a163451237016a7effb --- src/cython/cython/kernels.pyx | 47 ++++++++++++++++++++++++++++++++ src/cython/gudhi.pyx.in | 1 + src/cython/include/Kernels_interface.h | 49 ++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/cython/cython/kernels.pyx create mode 100644 src/cython/include/Kernels_interface.h diff --git a/src/cython/cython/kernels.pyx b/src/cython/cython/kernels.pyx new file mode 100644 index 00000000..220fc6ce --- /dev/null +++ b/src/cython/cython/kernels.pyx @@ -0,0 +1,47 @@ +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.utility cimport pair +import os + +"""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): Mathieu Carriere + + Copyright (C) 2018 INRIA + + 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 . +""" + +__author__ = "Mathieu Carriere" +__copyright__ = "Copyright (C) 2018 INRIA" +__license__ = "GPL v3" + +cdef extern from "Kernels_interface.h" namespace "Gudhi::persistence_diagram": + double sw(vector[pair[double, double]], vector[pair[double, double]], double, int) + +def sliced_wasserstein(diagram_1, diagram_2, sigma = 1, N = 100): + """ + + :param diagram_1: The first diagram. + :type diagram_1: vector[pair[double, double]] + :param diagram_2: The second diagram. + :type diagram_2: vector[pair[double, double]] + :param sigma: bandwidth of Gaussian + :param N: number of directions + + :returns: the sliced wasserstein kernel. + """ + return sw(diagram_1, diagram_2, sigma, N) diff --git a/src/cython/gudhi.pyx.in b/src/cython/gudhi.pyx.in index a8dd9f80..7f42968d 100644 --- a/src/cython/gudhi.pyx.in +++ b/src/cython/gudhi.pyx.in @@ -36,6 +36,7 @@ include '@CMAKE_CURRENT_SOURCE_DIR@/cython/persistence_graphical_tools.py' include '@CMAKE_CURRENT_SOURCE_DIR@/cython/reader_utils.pyx' include '@CMAKE_CURRENT_SOURCE_DIR@/cython/witness_complex.pyx' include '@CMAKE_CURRENT_SOURCE_DIR@/cython/strong_witness_complex.pyx' +include '@CMAKE_CURRENT_SOURCE_DIR@/cython/kernels.pyx' @GUDHI_CYTHON_ALPHA_COMPLEX@ @GUDHI_CYTHON_EUCLIDEAN_WITNESS_COMPLEX@ @GUDHI_CYTHON_SUBSAMPLING@ diff --git a/src/cython/include/Kernels_interface.h b/src/cython/include/Kernels_interface.h new file mode 100644 index 00000000..9eb610b0 --- /dev/null +++ b/src/cython/include/Kernels_interface.h @@ -0,0 +1,49 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 INRIA + * + * 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 INCLUDE_KERNELS_INTERFACE_H_ +#define INCLUDE_KERNELS_INTERFACE_H_ + +#include + +#include +#include +#include // for std::pair + +namespace Gudhi { + +namespace persistence_diagram { + + double sw(const std::vector>& diag1, + const std::vector>& diag2, + double sigma, int N) { + Gudhi::Persistence_representations::Sliced_Wasserstein sw1(diag1, sigma, N); + Gudhi::Persistence_representations::Sliced_Wasserstein sw2(diag2, sigma, N); + return sw1.compute_scalar_product(sw2); + } + +} // namespace persistence_diagram + +} // namespace Gudhi + + +#endif // INCLUDE_KERNELS_INTERFACE_H_ -- cgit v1.2.3 From 784697ab263e30c062e92aacfce36d1ed4070c6f Mon Sep 17 00:00:00 2001 From: mcarrier Date: Tue, 6 Mar 2018 17:50:39 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3269 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 17860628d3250f689152cdf65432c5a61d76f4d2 --- .../example/sliced_wasserstein.cpp | 2 + .../include/gudhi/Sliced_Wasserstein.h | 63 +++++++++++++++------- src/cython/cython/kernels.pyx | 17 +++++- src/cython/include/Kernels_interface.h | 15 ++++++ 4 files changed, 77 insertions(+), 20 deletions(-) diff --git a/src/Persistence_representations/example/sliced_wasserstein.cpp b/src/Persistence_representations/example/sliced_wasserstein.cpp index f153fbe8..2470029b 100644 --- a/src/Persistence_representations/example/sliced_wasserstein.cpp +++ b/src/Persistence_representations/example/sliced_wasserstein.cpp @@ -32,6 +32,8 @@ int main(int argc, char** argv) { std::vector > persistence1; std::vector > persistence2; + std::vector > > set1; + std::vector > > set2; persistence1.push_back(std::make_pair(1, 2)); persistence1.push_back(std::make_pair(6, 8)); diff --git a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h index 6196e207..f2ec56b7 100644 --- a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h +++ b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h @@ -51,15 +51,47 @@ class Sliced_Wasserstein { PD diagram; int approx; double sigma; + std::vector > projections, projections_diagonal; + public: - Sliced_Wasserstein(PD _diagram){diagram = _diagram; approx = 100; sigma = 0.001;} - Sliced_Wasserstein(PD _diagram, double _sigma, int _approx){diagram = _diagram; approx = _approx; sigma = _sigma;} + void build_rep(){ + + if(approx > 0){ + + double step = pi/this->approx; + int n = diagram.size(); + + for (int i = 0; i < this->approx; i++){ + std::vector l,l_diag; + for (int j = 0; j < n; j++){ + + double px = diagram[j].first; double py = diagram[j].second; + double proj_diag = (px+py)/2; + + l.push_back ( px * cos(-pi/2+i*step) + py * sin(-pi/2+i*step) ); + l_diag.push_back ( proj_diag * cos(-pi/2+i*step) + proj_diag * sin(-pi/2+i*step) ); + } + + std::sort(l.begin(), l.end()); std::sort(l_diag.begin(), l_diag.end()); + projections.push_back(l); projections_diagonal.push_back(l_diag); + + } + + } + + } + + Sliced_Wasserstein(PD _diagram){diagram = _diagram; approx = 100; sigma = 0.001; build_rep();} + Sliced_Wasserstein(PD _diagram, double _sigma, int _approx){diagram = _diagram; approx = _approx; sigma = _sigma; build_rep();} + PD get_diagram(){return this->diagram;} int get_approx(){return this->approx;} double get_sigma(){return this->sigma;} + + // ********************************** // Utils. @@ -227,28 +259,19 @@ class Sliced_Wasserstein { else{ + double step = pi/this->approx; - // Add projections onto diagonal. - int n1, n2; n1 = diagram1.size(); n2 = diagram2.size(); - for (int i = 0; i < n2; i++) - diagram1.emplace_back( (diagram2[i].first + diagram2[i].second)/2, (diagram2[i].first + diagram2[i].second)/2 ); - for (int i = 0; i < n1; i++) - diagram2.emplace_back( (diagram1[i].first + diagram1[i].second)/2, (diagram1[i].first + diagram1[i].second)/2 ); - int n = diagram1.size(); - - // Sort and compare all projections. for (int i = 0; i < this->approx; i++){ - std::vector > l1, l2; - for (int j = 0; j < n; j++){ - l1.emplace_back( j, diagram1[j].first*cos(-pi/2+i*step) + diagram1[j].second*sin(-pi/2+i*step) ); - l2.emplace_back( j, diagram2[j].first*cos(-pi/2+i*step) + diagram2[j].second*sin(-pi/2+i*step) ); - } - std::sort(l1.begin(),l1.end(), [=](const std::pair & p1, const std::pair & p2){return p1.second < p2.second;}); - std::sort(l2.begin(),l2.end(), [=](const std::pair & p1, const std::pair & p2){return p1.second < p2.second;}); - double f = 0; for (int j = 0; j < n; j++) f += std::abs(l1[j].second - l2[j].second); + + std::vector v1; std::vector l1 = this->projections[i]; std::vector l1bis = second.projections_diagonal[i]; std::merge(l1.begin(), l1.end(), l1bis.begin(), l1bis.end(), std::back_inserter(v1)); + std::vector v2; std::vector l2 = second.projections[i]; std::vector l2bis = this->projections_diagonal[i]; std::merge(l2.begin(), l2.end(), l2bis.begin(), l2bis.end(), std::back_inserter(v2)); + int n = v1.size(); double f = 0; + for (int j = 0; j < n; j++) f += std::abs(v1[j] - v2[j]); sw += f*step; + } + } return sw/pi; @@ -265,6 +288,8 @@ class Sliced_Wasserstein { } + + }; } // namespace Sliced_Wasserstein diff --git a/src/cython/cython/kernels.pyx b/src/cython/cython/kernels.pyx index 220fc6ce..f8798aab 100644 --- a/src/cython/cython/kernels.pyx +++ b/src/cython/cython/kernels.pyx @@ -30,7 +30,8 @@ __copyright__ = "Copyright (C) 2018 INRIA" __license__ = "GPL v3" cdef extern from "Kernels_interface.h" namespace "Gudhi::persistence_diagram": - double sw(vector[pair[double, double]], vector[pair[double, double]], double, int) + double sw (vector[pair[double, double]], vector[pair[double, double]], double, int) + vector[vector[double]] sw_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) def sliced_wasserstein(diagram_1, diagram_2, sigma = 1, N = 100): """ @@ -45,3 +46,17 @@ def sliced_wasserstein(diagram_1, diagram_2, sigma = 1, N = 100): :returns: the sliced wasserstein kernel. """ return sw(diagram_1, diagram_2, sigma, N) + +def sliced_wasserstein_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100): + """ + + :param diagram_1: The first set of diagrams. + :type diagram_1: vector[vector[pair[double, double]]] + :param diagram_2: The second set of diagrams. + :type diagram_2: vector[vector[pair[double, double]]] + :param sigma: bandwidth of Gaussian + :param N: number of directions + + :returns: the sliced wasserstein kernel matrix. + """ + return sw_matrix(diagrams_1, diagrams_2, sigma, N) diff --git a/src/cython/include/Kernels_interface.h b/src/cython/include/Kernels_interface.h index 9eb610b0..ef136731 100644 --- a/src/cython/include/Kernels_interface.h +++ b/src/cython/include/Kernels_interface.h @@ -41,6 +41,21 @@ namespace persistence_diagram { return sw1.compute_scalar_product(sw2); } + std::vector > sw_matrix(const std::vector > >& s1, + const std::vector > >& s2, + double sigma, int N){ + std::vector > matrix; + std::vector ss1; + int num_diag_1 = s1.size(); for(int i = 0; i < num_diag_1; i++){Gudhi::Persistence_representations::Sliced_Wasserstein sw1(s1[i], sigma, N); ss1.push_back(sw1);} + std::vector ss2; + int num_diag_2 = s2.size(); for(int i = 0; i < num_diag_2; i++){Gudhi::Persistence_representations::Sliced_Wasserstein sw2(s2[i], sigma, N); ss2.push_back(sw2);} + for(int i = 0; i < num_diag_1; i++){ + std::cout << 100.0*i/num_diag_1 << " %" << std::endl; + std::vector ps; for(int j = 0; j < num_diag_2; j++) ps.push_back(ss1[i].compute_scalar_product(ss2[j])); matrix.push_back(ps); + } + return matrix; + } + } // namespace persistence_diagram } // namespace Gudhi -- cgit v1.2.3 From 4e80b66cf5d4e6121149a12f3137e372e04d8588 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Thu, 29 Mar 2018 15:27:31 +0000 Subject: added doc + cython git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3319 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: cffc2e28ebf6fae46246c5abaac52b7328adf490 --- .../doc/Persistence_representations_doc.h | 62 +++++++++++++++++++ .../example/persistence_weighted_gaussian.cpp | 16 ++--- .../example/sliced_wasserstein.cpp | 12 ++-- .../include/gudhi/Persistence_weighted_gaussian.h | 72 +++++++++++++++++++--- .../include/gudhi/Sliced_Wasserstein.h | 58 +++++++++++++++-- src/cython/cython/kernels.pyx | 56 +++++++++++++++++ src/cython/include/Kernels_interface.h | 65 +++++++++++++++++++ 7 files changed, 315 insertions(+), 26 deletions(-) diff --git a/src/Persistence_representations/doc/Persistence_representations_doc.h b/src/Persistence_representations/doc/Persistence_representations_doc.h index 38bd3a21..6d4cc96c 100644 --- a/src/Persistence_representations/doc/Persistence_representations_doc.h +++ b/src/Persistence_representations/doc/Persistence_representations_doc.h @@ -250,6 +250,68 @@ namespace Persistence_representations { absolute value of differences between coordinates. A scalar product is a sum of products of values at the corresponding positions of two vectors. + + + + +\section sec_persistence_kernels Kernels on Persistence Diagrams + Reference manual: \ref Gudhi::Persistence_representations::Sliced_Wasserstein
+ Reference manual: \ref Gudhi::Persistence_representations::Persistence_weighted_gaussian
+ + Kernels for Persistence Diagrams can be regarded as infinite-dimensional vectorizations. More specifically, + they are similarity functions whose evaluations on pairs of persistence diagrams equals the scalar products + between images of these pairs under a map \f$\Phi\f$ taking values in a specific (possibly non Euclidean) Hilbert space \f$k(D_i, D_j) = \langle \Phi(D_i),\Phi(D_j)\rangle\f$. + Reciprocally, classical results of learning theory ensure that such a \f$\Phi\f$ exists for a given similarity function \f$k\f$ if and only if \f$k\f$ is positive semi-definite. + Kernels are designed for algorithms that can be kernelized, i.e., algorithms that only require to know scalar products between instances in order to run. + Examples of such algorithms include Support Vector Machines, Principal Component Analysis and Ridge Regression. + + There have been several attempts at defining kernels, i.e., positive semi-definite functions, between persistence diagrams within the last few years. We provide implementation + for three of them: + + \li the Persistence Scale Space Kernel---see \cite Reininghaus_Huber_ALL_PSSK, which is the classical scalar product between \f$L^2\f$ functions, where persistence diagrams + are turned into functions by centering and summing Gaussian functions over the diagram points and their symmetric counterparts w.r.t. the diagonal: \f$k(D_1,D_2)=\int \Phi(D_1)\Phi(D_2)\f$, + where \f$\Phi(D)=\sum_{p\in D} {\rm exp}\left(-\frac{\|p-\cdot\|_2^2}{2\sigma^2}\right)\f$. + + \li the Persistence Weighted Gaussian Kernel---see \cite Kusano_Fukumizu_Hiraoka_PWGK, which is a slight generalization of the previous kernel, is the scalar product between + weighted Kernel Mean Embeddings of persistence diagrams w.r.t. the Gaussian Kernel \f$k_G\f$ (with corresponding map \f$\Phi_G\f$) in \f$\mathbb{R}^2\f$: + \f$k(D_1,D_2)=\langle\sum_{p\in D_1} w(p)\Phi_G(p), \sum_{q\in D_2} w(q)\Phi_G(q)\rangle\f$ + + \li the Sliced Wasserstein Kernel---see \cite pmlr-v70-carriere17a, which takes the form of a Gaussian kernel with a specific distance between persistence diagrams + called the Sliced Wasserstein Distance: \f$k(D_1,D_2)={\rm exp}\left(-\frac{SW(D_1,D_2)}{2\sigma^2}\right)\f$ + + When launching: + + \code $> ./Sliced_Wasserstein + \endcode + + the program output is: + + \code $> Approx SW distance: 5.33648 + $> Exact SW distance: 5.33798 + $> Approx SW kernel: 0.0693743 + $> Exact SW kernel: 0.0693224 + $> Distance induced by approx SW kernel: 1.36428 + $> Distance induced by exact SW kernel: 1.3643 + \endcode + + + and when launching: + + \code $> ./Persistence_weighted_gaussian + \endcode + + the program output is: + + \code $> Approx PWG kernel: 1.21509 + $> Exact PWG kernel: 1.13628 + $> Distance induced by approx PWG kernel: 3.23354 + $> Distance induced by exact PWG kernel: 3.25697 + $> Approx Gaussian PWG kernel: 0.0194222 + $> Exact Gaussian PWG kernel: 0.0192524 + $> Approx PSS kernel: 0.134413 + $> Exact PSS kernel: 0.133394 + \endcode + */ /** @} */ // end defgroup Persistence_representations diff --git a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp index a0e820ea..d447f165 100644 --- a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp +++ b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp @@ -57,11 +57,11 @@ int main(int argc, char** argv) { // Linear PWG - std::cout << PWG1.compute_scalar_product (PWG2) << std::endl; - std::cout << PWGex1.compute_scalar_product (PWGex2) << std::endl; + std::cout << "Approx PWG kernel: " << PWG1.compute_scalar_product (PWG2) << std::endl; + std::cout << "Exact PWG kernel: " << PWGex1.compute_scalar_product (PWGex2) << std::endl; - std::cout << PWG1.distance (PWG2) << std::endl; - std::cout << PWGex1.distance (PWGex2) << std::endl; + std::cout << "Distance induced by approx PWG kernel: " << PWG1.distance (PWG2) << std::endl; + std::cout << "Distance induced by exact PWG kernel: " << PWGex1.distance (PWGex2) << std::endl; @@ -71,8 +71,8 @@ int main(int argc, char** argv) { // Gaussian PWG - std::cout << std::exp( -PWG1.distance (PWG2, 2) ) / (2*tau*tau) << std::endl; - std::cout << std::exp( -PWGex1.distance (PWGex2, 2) ) / (2*tau*tau) << std::endl; + std::cout << "Approx Gaussian PWG kernel: " << std::exp( -PWG1.distance (PWG2) ) / (2*tau*tau) << std::endl; + std::cout << "Exact Gaussian PWG kernel: " << std::exp( -PWGex1.distance (PWGex2) ) / (2*tau*tau) << std::endl; @@ -91,8 +91,8 @@ int main(int argc, char** argv) { PWG pwgex1(pd1, 2*std::sqrt(sigma), -1, PWG::pss_weight); PWG pwgex2(pd2, 2*std::sqrt(sigma), -1, PWG::pss_weight); - std::cout << pwg1.compute_scalar_product (pwg2) / (16*pi*sigma) << std::endl; - std::cout << pwgex1.compute_scalar_product (pwgex2) / (16*pi*sigma) << std::endl; + std::cout << "Approx PSS kernel: " << pwg1.compute_scalar_product (pwg2) / (16*pi*sigma) << std::endl; + std::cout << "Exact PSS kernel: " << pwgex1.compute_scalar_product (pwgex2) / (16*pi*sigma) << std::endl; diff --git a/src/Persistence_representations/example/sliced_wasserstein.cpp b/src/Persistence_representations/example/sliced_wasserstein.cpp index 2470029b..f1aeea5c 100644 --- a/src/Persistence_representations/example/sliced_wasserstein.cpp +++ b/src/Persistence_representations/example/sliced_wasserstein.cpp @@ -32,8 +32,6 @@ int main(int argc, char** argv) { std::vector > persistence1; std::vector > persistence2; - std::vector > > set1; - std::vector > > set2; persistence1.push_back(std::make_pair(1, 2)); persistence1.push_back(std::make_pair(6, 8)); @@ -52,10 +50,12 @@ int main(int argc, char** argv) { SW swex1(persistence1, 1, -1); SW swex2(persistence2, 1, -1); - std::cout << sw1.compute_sliced_wasserstein_distance(sw2) << std::endl; - std::cout << swex1.compute_sliced_wasserstein_distance(swex2) << std::endl; - std::cout << sw1.compute_scalar_product(sw2) << std::endl; - std::cout << swex1.distance(swex2) << std::endl; + std::cout << "Approx SW distance: " << sw1.compute_sliced_wasserstein_distance(sw2) << std::endl; + std::cout << "Exact SW distance: " << swex1.compute_sliced_wasserstein_distance(swex2) << std::endl; + std::cout << "Approx SW kernel: " << sw1.compute_scalar_product(sw2) << std::endl; + std::cout << "Exact SW kernel: " << swex1.compute_scalar_product(swex2) << std::endl; + std::cout << "Distance induced by approx SW kernel: " << sw1.distance(sw2) << std::endl; + std::cout << "Distance induced by exact SW kernel: " << swex1.distance(swex2) << std::endl; return 0; } diff --git a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h index a6efa72d..f824225a 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h +++ b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h @@ -45,7 +45,40 @@ using Weight = std::function) >; namespace Gudhi { namespace Persistence_representations { - +/** + * \class Persistence_weighted_gaussian gudhi/Persistence_weighted_gaussian.h + * \brief A class implementing the Persistence Weighted Gaussian Kernel and a specific case of it called the Persistence Scale Space Kernel. + * + * \ingroup Persistence_representations + * + * \details + * The Persistence Weighted Gaussian Kernel is built with Gaussian Kernel Mean Embedding, meaning that each persistence diagram is first + * sent to the Hilbert space of a Gaussian kernel with bandwidth parameter \f$\sigma >0\f$ using a weighted mean embedding \f$\Phi\f$: + * + * \f$ \Phi\,:\,D\,\rightarrow\,\sum_{p\in D}\,w(p)\,{\rm exp}\left(-\frac{\|p-\cdot\|_2^2}{2\sigma^2}\right) \f$, + * + * Usually, the weight function is chosen to be an arctan function of the distance of the point to the diagonal: + * \f$w(p) = {\rm arctan}(C\,|y-x|^\alpha)\f$, for some parameters \f$C,\alpha >0\f$. + * Then, their scalar product in this space is computed: + * + * \f$ k(D_1,D_2)=\langle\Phi(D_1),\Phi(D_2)\rangle + * \,=\,\sum_{p\in D_1}\,\sum_{q\in D_2}\,w(p)\,w(q)\,{\rm exp}\left(-\frac{\|p-q\|_2^2}{2\sigma^2}\right).\f$ + * + * Note that one may apply a second Gaussian kernel to their distance in this space and still get a kernel. + * + * It follows that the computation time is \f$O(n^2)\f$ where \f$n\f$ is the number of points + * in the diagrams. This time can be improved by computing approximations of the kernel + * with \f$m\f$ Fourier features \cite Rahimi07randomfeatures. In that case, the computation time becomes \f$O(mn)\f$. + * + * The Persistence Scale Space Kernel is a Persistence Weighted Gaussian Kernel between modified diagrams: + * the symmetric of each point with respect to the diagonal is first added in each diagram, and then the weight function + * is set to be +1 if the point is above the diagonal and -1 otherwise. + * + * For more details, please consult Persistence Weighted Kernel for Topological Data Analysis\cite Kusano_Fukumizu_Hiraoka_PWGK + * and A Stable Multi-Scale Kernel for Topological Machine Learning\cite Reininghaus_Huber_ALL_PSSK . + * It implements the following concepts: Topological_data_with_distances, Topological_data_with_scalar_product. + * +**/ class Persistence_weighted_gaussian{ protected: @@ -56,8 +89,17 @@ class Persistence_weighted_gaussian{ public: - Persistence_weighted_gaussian(PD _diagram){diagram = _diagram; sigma = 1.0; approx = 1000; weight = arctan_weight;} - Persistence_weighted_gaussian(PD _diagram, double _sigma, int _approx, Weight _weight){diagram = _diagram; sigma = _sigma; approx = _approx; weight = _weight;} + /** \brief Persistence Weighted Gaussian Kernel constructor. + * \ingroup Persistence_weighted_gaussian + * + * @param[in] _diagram persistence diagram. + * @param[in] _sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. + * @param[in] _approx number of random Fourier features in case of approximate computation, set to -1 for exact computation. + * @param[in] _weight weight function for the points in the diagrams. + * + */ + Persistence_weighted_gaussian(PD _diagram, double _sigma = 1.0, int _approx = 1000, Weight _weight = arctan_weight){diagram = _diagram; sigma = _sigma; approx = _approx; weight = _weight;} + PD get_diagram(){return this->diagram;} double get_sigma(){return this->sigma;} int get_approx(){return this->approx;} @@ -68,7 +110,12 @@ class Persistence_weighted_gaussian{ // Utils. // ********************************** - + /** \brief Specific weight of Persistence Scale Space Kernel. + * \ingroup Persistence_weighted_gaussian + * + * @param[in] p point in 2D. + * + */ static double pss_weight(std::pair p){ if(p.second > p.first) return 1; else return -1; @@ -108,7 +155,12 @@ class Persistence_weighted_gaussian{ // Scalar product + distance. // ********************************** - + /** \brief Evaluation of the kernel on a pair of diagrams. + * \ingroup Persistence_weighted_gaussian + * + * @param[in] second other instance of class Persistence_weighted_gaussian. Warning: sigma, approx and weight parameters need to be the same for both instances!!! + * + */ double compute_scalar_product(Persistence_weighted_gaussian second){ PD diagram1 = this->diagram; PD diagram2 = second.diagram; @@ -131,11 +183,17 @@ class Persistence_weighted_gaussian{ } } - double distance(Persistence_weighted_gaussian second, double power = 1) { + /** \brief Evaluation of the distance between images of diagrams in the Hilbert space of the kernel. + * \ingroup Persistence_weighted_gaussian + * + * @param[in] second other instance of class Persistence_weighted_gaussian. Warning: sigma, approx and weight parameters need to be the same for both instances!!! + * + */ + double distance(Persistence_weighted_gaussian second) { if(this->sigma != second.get_sigma() || this->approx != second.get_approx()){ std::cout << "Error: different representations!" << std::endl; return 0; } - else return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), power/2.0); + else return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), 0.5); } diff --git a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h index f2ec56b7..bfb77384 100644 --- a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h +++ b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h @@ -45,6 +45,30 @@ using PD = std::vector >; namespace Gudhi { namespace Persistence_representations { +/** + * \class Sliced_Wasserstein gudhi/Sliced_Wasserstein.h + * \brief A class implementing the Sliced Wasserstein Kernel. + * + * \ingroup Persistence_representations + * + * \details + * The Sliced Wasserstein Kernel is defined as a Gaussian-like Kernel between persistence diagrams, where the distance used for + * comparison is the Sliced Wasserstein distance \f$SW\f$ between persistence diagrams, defined as the integral of the 1-norm + * between the sorted projections of the diagrams onto all lines passing through the origin: + * + * \f$ SW(D_1,D_2)=\int_{\theta\in\mathbb{S}}\,\|\pi_\theta(D_1\cup\pi_\Delta(D_2))-\pi_\theta(D_2\cup\pi_\Delta(D_1))\|_1{\rm d}\theta\f$, + * + * where \f$\pi_\theta\f$ is the projection onto the line defined with angle \f$\theta\f$ in the unit circle \f$\mathbb{S}\f$, + * and \f$\pi_\Delta\f$ is the projection onto the diagonal. + * The integral can be either computed exactly in \f$O(n^2{\rm log}(n))\f$ time, where \f$n\f$ is the number of points + * in the diagrams, or approximated by sampling \f$N\f$ lines in the circle in \f$O(Nn{\rm log}(n))\f$ time. The Sliced Wasserstein Kernel is then computed as: + * + * \f$ k(D_1,D_2) = {\rm exp}\left(-\frac{SW(D_1,D_2)}{2\sigma^2}\right).\f$ + * + * For more details, please consult Sliced Wasserstein Kernel for Persistence Diagrams\cite pmlr-v70-carriere17a . + * It implements the following concepts: Topological_data_with_distances, Topological_data_with_scalar_product. + * +**/ class Sliced_Wasserstein { protected: @@ -83,8 +107,15 @@ class Sliced_Wasserstein { } - Sliced_Wasserstein(PD _diagram){diagram = _diagram; approx = 100; sigma = 0.001; build_rep();} - Sliced_Wasserstein(PD _diagram, double _sigma, int _approx){diagram = _diagram; approx = _approx; sigma = _sigma; build_rep();} + /** \brief Sliced Wasserstein Kernel constructor. + * \ingroup Sliced_Wasserstein + * + * @param[in] _diagram persistence diagram. + * @param[in] _sigma bandwidth parameter. + * @param[in] _approx number of directions used to approximate the integral in the Sliced Wasserstein distance, set to -1 for exact computation. + * + */ + Sliced_Wasserstein(PD _diagram, double _sigma = 1.0, int _approx = 100){diagram = _diagram; approx = _approx; sigma = _sigma; build_rep();} PD get_diagram(){return this->diagram;} int get_approx(){return this->approx;} @@ -163,6 +194,12 @@ class Sliced_Wasserstein { // Scalar product + distance. // ********************************** + /** \brief Evaluation of the Sliced Wasserstein Distance between a pair of diagrams. + * \ingroup Sliced_Wasserstein + * + * @param[in] second other instance of class Sliced_Wasserstein. Warning: approx parameter needs to be the same for both instances!!! + * + */ double compute_sliced_wasserstein_distance(Sliced_Wasserstein second) { PD diagram1 = this->diagram; PD diagram2 = second.diagram; double sw = 0; @@ -277,14 +314,25 @@ class Sliced_Wasserstein { return sw/pi; } - + /** \brief Evaluation of the kernel on a pair of diagrams. + * \ingroup Sliced_Wasserstein + * + * @param[in] second other instance of class Sliced_Wasserstein. Warning: sigma and approx parameters need to be the same for both instances!!! + * + */ double compute_scalar_product(Sliced_Wasserstein second){ return std::exp(-compute_sliced_wasserstein_distance(second)/(2*this->sigma*this->sigma)); } - double distance(Sliced_Wasserstein second, double power = 1) { + /** \brief Evaluation of the distance between images of diagrams in the Hilbert space of the kernel. + * \ingroup Sliced_Wasserstein + * + * @param[in] second other instance of class Sliced_Wasserstein. Warning: sigma and approx parameters need to be the same for both instances!!! + * + */ + double distance(Sliced_Wasserstein second) { if(this->sigma != second.sigma || this->approx != second.approx){std::cout << "Error: different representations!" << std::endl; return 0;} - else return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), power/2.0); + else return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), 0.5); } diff --git a/src/cython/cython/kernels.pyx b/src/cython/cython/kernels.pyx index f8798aab..4fc21f03 100644 --- a/src/cython/cython/kernels.pyx +++ b/src/cython/cython/kernels.pyx @@ -60,3 +60,59 @@ def sliced_wasserstein_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100): :returns: the sliced wasserstein kernel matrix. """ return sw_matrix(diagrams_1, diagrams_2, sigma, N) + +def persistence_weighted_gaussian(diagram_1, diagram_2, sigma = 1, N = 100): + """ + + :param diagram_1: The first diagram. + :type diagram_1: vector[pair[double, double]] + :param diagram_2: The second diagram. + :type diagram_2: vector[pair[double, double]] + :param sigma: bandwidth of Gaussian + :param N: number of Fourier features + + :returns: the persistence weighted gaussian kernel. + """ + return pwg(diagram_1, diagram_2, sigma, N) + +def persistence_weighted_gaussian_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100): + """ + + :param diagram_1: The first set of diagrams. + :type diagram_1: vector[vector[pair[double, double]]] + :param diagram_2: The second set of diagrams. + :type diagram_2: vector[vector[pair[double, double]]] + :param sigma: bandwidth of Gaussian + :param N: number of Fourier features + + :returns: the persistence weighted gaussian kernel matrix. + """ + return pwg_matrix(diagrams_1, diagrams_2, sigma, N) + +def persistence_scale_space(diagram_1, diagram_2, sigma = 1, N = 100): + """ + + :param diagram_1: The first diagram. + :type diagram_1: vector[pair[double, double]] + :param diagram_2: The second diagram. + :type diagram_2: vector[pair[double, double]] + :param sigma: bandwidth of Gaussian + :param N: number of Fourier features + + :returns: the persistence scale space kernel. + """ + return pss(diagram_1, diagram_2, sigma, N) + +def persistence_scale_space_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100): + """ + + :param diagram_1: The first set of diagrams. + :type diagram_1: vector[vector[pair[double, double]]] + :param diagram_2: The second set of diagrams. + :type diagram_2: vector[vector[pair[double, double]]] + :param sigma: bandwidth of Gaussian + :param N: number of Fourier features + + :returns: the persistence scale space kernel matrix. + """ + return pss_matrix(diagrams_1, diagrams_2, sigma, N) diff --git a/src/cython/include/Kernels_interface.h b/src/cython/include/Kernels_interface.h index ef136731..17bb5d8b 100644 --- a/src/cython/include/Kernels_interface.h +++ b/src/cython/include/Kernels_interface.h @@ -41,6 +41,38 @@ namespace persistence_diagram { return sw1.compute_scalar_product(sw2); } + double pwg(const std::vector>& diag1, + const std::vector>& diag2, + double sigma, int N) { + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, sigma, N, Gudhi::Persistence_representations::arctan_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, sigma, N, Gudhi::Persistence_representations::arctan_weight); + return pwg1.compute_scalar_product(pwg2); + } + + double pss(const std::vector>& diag1, + const std::vector>& diag2, + double sigma, int N) { + + std::vector> pd1 = diag1; int numpts = diag1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(diag1[i].second,diag1[i].first); + std::vector> pd2 = diag2; numpts = diag2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(diag2[i].second,diag2[i].first); + + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(pd1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(pd2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); + + return pwg1.compute_scalar_product (pwg2) / (16*pi*sigma); + } + + double pss_sym(const std::vector>& diag1, + const std::vector>& diag2, + double sigma, int N) { + + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(pd1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(pd2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); + + return pwg1.compute_scalar_product (pwg2) / (16*pi*sigma); + } + + std::vector > sw_matrix(const std::vector > >& s1, const std::vector > >& s2, double sigma, int N){ @@ -56,6 +88,39 @@ namespace persistence_diagram { return matrix; } + std::vector > pwg_matrix(const std::vector > >& s1, + const std::vector > >& s2, + double sigma, int N){ + std::vector > matrix; int num_diag_1 = s1.size(); int num_diag_2 = s2.size(); + for(int i = 0; i < num_diag_1; i++){ + std::cout << 100.0*i/num_diag_1 << " %" << std::endl; + std::vector ps; for(int j = 0; j < num_diag_2; j++) ps.push_back(pwg(s1[i], s2[j], sigma, N)); matrix.push_back(ps); + } + return matrix; + } + + std::vector > pss_matrix(const std::vector > >& s1, + const std::vector > >& s2, + double sigma, int N){ + std::vector > > ss1, ss2; + std::vector > matrix; int num_diag_1 = s1.size(); int num_diag_2 = s2.size(); + for(int i = 0; i < num_diag_1; i++){ + std::vector> pd1 = s1[i]; int numpts = s1[i].size(); + for(int j = 0; j < numpts; j++) pd1.emplace_back(s1[i][j].second,s1[i][j].first); + ss1.push_back(pd1); + + for(int i = 0; i < num_diag_2; i++){ + std::vector> pd2 = s2[i]; int numpts = s2[i].size(); + for(int j = 0; j < numpts; j++) pd2.emplace_back(s2[i][j].second,s2[i][j].first); + ss2.push_back(pd2); + + for(int i = 0; i < num_diag_1; i++){ + std::cout << 100.0*i/num_diag_1 << " %" << std::endl; + std::vector ps; for(int j = 0; j < num_diag_2; j++) ps.push_back(pss_sym(ss1[i], ss2[j], sigma, N)); matrix.push_back(ps); + } + return matrix; + } + } // namespace persistence_diagram } // namespace Gudhi -- cgit v1.2.3 From 860fa7d916cb591cb0b016b046077d5333570731 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Fri, 30 Mar 2018 18:09:57 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3323 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 550a4c3f9050b542203ab53ea7ef5ae1c8032f38 --- .../include/gudhi/Persistence_weighted_gaussian.h | 3 ++- .../include/gudhi/Sliced_Wasserstein.h | 6 +++--- src/cython/cython/kernels.pyx | 4 ++++ src/cython/include/Kernels_interface.h | 19 +++++++++++-------- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h index f824225a..d5c8e6d7 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h +++ b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h @@ -39,7 +39,6 @@ #include #include -double pi = boost::math::constants::pi(); using PD = std::vector >; using Weight = std::function) >; @@ -88,6 +87,8 @@ class Persistence_weighted_gaussian{ int approx; public: + + double pi = boost::math::constants::pi(); /** \brief Persistence Weighted Gaussian Kernel constructor. * \ingroup Persistence_weighted_gaussian diff --git a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h index bfb77384..fc3cd142 100644 --- a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h +++ b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h @@ -39,7 +39,6 @@ #include #include -double pi = boost::math::constants::pi(); using PD = std::vector >; namespace Gudhi { @@ -77,13 +76,14 @@ class Sliced_Wasserstein { double sigma; std::vector > projections, projections_diagonal; - public: + double pi = boost::math::constants::pi(); + void build_rep(){ if(approx > 0){ - + double step = pi/this->approx; int n = diagram.size(); diff --git a/src/cython/cython/kernels.pyx b/src/cython/cython/kernels.pyx index 4fc21f03..466917b1 100644 --- a/src/cython/cython/kernels.pyx +++ b/src/cython/cython/kernels.pyx @@ -32,6 +32,10 @@ __license__ = "GPL v3" cdef extern from "Kernels_interface.h" namespace "Gudhi::persistence_diagram": double sw (vector[pair[double, double]], vector[pair[double, double]], double, int) vector[vector[double]] sw_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) + double pss (vector[pair[double, double]], vector[pair[double, double]], double, int) + vector[vector[double]] pss_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) + double pwg (vector[pair[double, double]], vector[pair[double, double]], double, int) + vector[vector[double]] pwg_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) def sliced_wasserstein(diagram_1, diagram_2, sigma = 1, N = 100): """ diff --git a/src/cython/include/Kernels_interface.h b/src/cython/include/Kernels_interface.h index 17bb5d8b..33cd6e35 100644 --- a/src/cython/include/Kernels_interface.h +++ b/src/cython/include/Kernels_interface.h @@ -24,6 +24,7 @@ #define INCLUDE_KERNELS_INTERFACE_H_ #include +#include #include #include @@ -44,20 +45,20 @@ namespace persistence_diagram { double pwg(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, sigma, N, Gudhi::Persistence_representations::arctan_weight); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, sigma, N, Gudhi::Persistence_representations::arctan_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, sigma, N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, sigma, N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight); return pwg1.compute_scalar_product(pwg2); } double pss(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { - + double pi = boost::math::constants::pi(); std::vector> pd1 = diag1; int numpts = diag1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(diag1[i].second,diag1[i].first); std::vector> pd2 = diag2; numpts = diag2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(diag2[i].second,diag2[i].first); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(pd1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(pd2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(pd1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(pd2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); return pwg1.compute_scalar_product (pwg2) / (16*pi*sigma); } @@ -65,9 +66,9 @@ namespace persistence_diagram { double pss_sym(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { - - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(pd1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(pd2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); + double pi = boost::math::constants::pi(); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); return pwg1.compute_scalar_product (pwg2) / (16*pi*sigma); } @@ -108,11 +109,13 @@ namespace persistence_diagram { std::vector> pd1 = s1[i]; int numpts = s1[i].size(); for(int j = 0; j < numpts; j++) pd1.emplace_back(s1[i][j].second,s1[i][j].first); ss1.push_back(pd1); + } for(int i = 0; i < num_diag_2; i++){ std::vector> pd2 = s2[i]; int numpts = s2[i].size(); for(int j = 0; j < numpts; j++) pd2.emplace_back(s2[i][j].second,s2[i][j].first); ss2.push_back(pd2); + } for(int i = 0; i < num_diag_1; i++){ std::cout << 100.0*i/num_diag_1 << " %" << std::endl; -- cgit v1.2.3 From 905be209a0e62121c125c37e01f4d2eae5aa606d Mon Sep 17 00:00:00 2001 From: mcarrier Date: Thu, 12 Apr 2018 15:22:45 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3378 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 7cd9f2ae7c9da5d525bdb76a00ffac1359a47da7 --- .../example/persistence_weighted_gaussian.cpp | 4 ++-- .../include/gudhi/Persistence_weighted_gaussian.h | 12 +++++------ .../include/gudhi/Sliced_Wasserstein.h | 23 +++++++++++++--------- .../gudhi/common_persistence_representations.h | 2 ++ src/cython/include/Kernels_interface.h | 6 ++---- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp index d447f165..dea5dab6 100644 --- a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp +++ b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp @@ -91,8 +91,8 @@ int main(int argc, char** argv) { PWG pwgex1(pd1, 2*std::sqrt(sigma), -1, PWG::pss_weight); PWG pwgex2(pd2, 2*std::sqrt(sigma), -1, PWG::pss_weight); - std::cout << "Approx PSS kernel: " << pwg1.compute_scalar_product (pwg2) / (16*pi*sigma) << std::endl; - std::cout << "Exact PSS kernel: " << pwgex1.compute_scalar_product (pwgex2) / (16*pi*sigma) << std::endl; + std::cout << "Approx PSS kernel: " << pwg1.compute_scalar_product (pwg2) / (16*Gudhi::Persistence_representations::pi*sigma) << std::endl; + std::cout << "Exact PSS kernel: " << pwgex1.compute_scalar_product (pwgex2) / (16*Gudhi::Persistence_representations::pi*sigma) << std::endl; diff --git a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h index d5c8e6d7..b30e0273 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h +++ b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h @@ -25,6 +25,7 @@ // gudhi include #include +#include // standard include #include @@ -37,7 +38,6 @@ #include #include #include -#include using PD = std::vector >; using Weight = std::function) >; @@ -87,8 +87,6 @@ class Persistence_weighted_gaussian{ int approx; public: - - double pi = boost::math::constants::pi(); /** \brief Persistence Weighted Gaussian Kernel constructor. * \ingroup Persistence_weighted_gaussian @@ -101,10 +99,10 @@ class Persistence_weighted_gaussian{ */ Persistence_weighted_gaussian(PD _diagram, double _sigma = 1.0, int _approx = 1000, Weight _weight = arctan_weight){diagram = _diagram; sigma = _sigma; approx = _approx; weight = _weight;} - PD get_diagram(){return this->diagram;} - double get_sigma(){return this->sigma;} - int get_approx(){return this->approx;} - Weight get_weight(){return this->weight;} + PD get_diagram() const {return this->diagram;} + double get_sigma() const {return this->sigma;} + int get_approx() const {return this->approx;} + Weight get_weight() const {return this->weight;} // ********************************** diff --git a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h index fc3cd142..6a9a607e 100644 --- a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h +++ b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h @@ -25,6 +25,8 @@ // gudhi include #include +#include +#include // standard include #include @@ -37,7 +39,6 @@ #include #include #include -#include using PD = std::vector >; @@ -68,6 +69,7 @@ namespace Persistence_representations { * It implements the following concepts: Topological_data_with_distances, Topological_data_with_scalar_product. * **/ + class Sliced_Wasserstein { protected: @@ -78,8 +80,6 @@ class Sliced_Wasserstein { public: - double pi = boost::math::constants::pi(); - void build_rep(){ if(approx > 0){ @@ -117,9 +117,9 @@ class Sliced_Wasserstein { */ Sliced_Wasserstein(PD _diagram, double _sigma = 1.0, int _approx = 100){diagram = _diagram; approx = _approx; sigma = _sigma; build_rep();} - PD get_diagram(){return this->diagram;} - int get_approx(){return this->approx;} - double get_sigma(){return this->sigma;} + PD get_diagram() const {return this->diagram;} + int get_approx() const {return this->approx;} + double get_sigma() const {return this->sigma;} @@ -197,11 +197,15 @@ class Sliced_Wasserstein { /** \brief Evaluation of the Sliced Wasserstein Distance between a pair of diagrams. * \ingroup Sliced_Wasserstein * - * @param[in] second other instance of class Sliced_Wasserstein. Warning: approx parameter needs to be the same for both instances!!! + * @param[in] second other instance of class Sliced_Wasserstein. + * For warning in red: + * @warning approx parameter needs to be the same for both instances. * */ double compute_sliced_wasserstein_distance(Sliced_Wasserstein second) { + GUDHI_CHECK(this->approx != second.approx, std::invalid_argument("Error: different approx values for representations")); + PD diagram1 = this->diagram; PD diagram2 = second.diagram; double sw = 0; if(this->approx == -1){ @@ -321,6 +325,7 @@ class Sliced_Wasserstein { * */ double compute_scalar_product(Sliced_Wasserstein second){ + GUDHI_CHECK(this->sigma != second.sigma, std::invalid_argument("Error: different sigma values for representations")); return std::exp(-compute_sliced_wasserstein_distance(second)/(2*this->sigma*this->sigma)); } @@ -331,8 +336,8 @@ class Sliced_Wasserstein { * */ double distance(Sliced_Wasserstein second) { - if(this->sigma != second.sigma || this->approx != second.approx){std::cout << "Error: different representations!" << std::endl; return 0;} - else return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), 0.5); + GUDHI_CHECK(this->sigma != second.sigma, std::invalid_argument("Error: different sigma values for representations")); + return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), 0.5); } diff --git a/src/Persistence_representations/include/gudhi/common_persistence_representations.h b/src/Persistence_representations/include/gudhi/common_persistence_representations.h index 44e125a7..90f2626d 100644 --- a/src/Persistence_representations/include/gudhi/common_persistence_representations.h +++ b/src/Persistence_representations/include/gudhi/common_persistence_representations.h @@ -26,11 +26,13 @@ #include #include #include +#include namespace Gudhi { namespace Persistence_representations { // this file contain an implementation of some common procedures used in Persistence_representations. +static constexpr double pi = boost::math::constants::pi(); // double epsi = std::numeric_limits::epsilon(); double epsi = 0.000005; diff --git a/src/cython/include/Kernels_interface.h b/src/cython/include/Kernels_interface.h index 33cd6e35..1742d016 100644 --- a/src/cython/include/Kernels_interface.h +++ b/src/cython/include/Kernels_interface.h @@ -53,24 +53,22 @@ namespace persistence_diagram { double pss(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { - double pi = boost::math::constants::pi(); std::vector> pd1 = diag1; int numpts = diag1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(diag1[i].second,diag1[i].first); std::vector> pd2 = diag2; numpts = diag2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(diag2[i].second,diag2[i].first); Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(pd1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(pd2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); - return pwg1.compute_scalar_product (pwg2) / (16*pi*sigma); + return pwg1.compute_scalar_product (pwg2) / (16*Gudhi::Persistence_representations::pi*sigma); } double pss_sym(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { - double pi = boost::math::constants::pi(); Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); - return pwg1.compute_scalar_product (pwg2) / (16*pi*sigma); + return pwg1.compute_scalar_product (pwg2) / (16*Gudhi::Persistence_representations::pi*sigma); } -- cgit v1.2.3 From 7f9e8f11f70e8387ef29c3fa13016121dca79cbe Mon Sep 17 00:00:00 2001 From: mcarrier Date: Fri, 20 Apr 2018 15:56:06 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3385 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 2a83e62e132ce406986efa1456b5f1bca6b93691 --- src/Kernels/doc/COPYRIGHT | 19 -- src/Kernels/doc/Intro_kernels.h | 108 ------ src/Kernels/example/CMakeLists.txt | 10 - src/Kernels/example/kernel.txt | 8 - src/Kernels/example/kernel_basic_example.cpp | 65 ---- src/Kernels/include/gudhi/kernel.h | 365 --------------------- src/Kernels/test/CMakeLists.txt | 12 - src/Kernels/test/test_kernel.cpp | 56 ---- .../example/CMakeLists.txt | 9 + .../example/landscape.cpp | 51 +++ .../example/persistence_image.cpp | 54 +++ .../example/persistence_weighted_gaussian.cpp | 8 +- .../include/gudhi/Landscape.h | 103 ++++++ .../include/gudhi/Persistence_image.h | 117 +++++++ .../include/gudhi/Persistence_weighted_gaussian.h | 15 +- src/cython/cython/kernels.pyx | 16 +- src/cython/cython/vectors.pyx | 65 ++++ src/cython/gudhi.pyx.in | 1 + src/cython/include/Kernels_interface.h | 46 ++- 19 files changed, 440 insertions(+), 688 deletions(-) delete mode 100644 src/Kernels/doc/COPYRIGHT delete mode 100644 src/Kernels/doc/Intro_kernels.h delete mode 100644 src/Kernels/example/CMakeLists.txt delete mode 100644 src/Kernels/example/kernel.txt delete mode 100644 src/Kernels/example/kernel_basic_example.cpp delete mode 100644 src/Kernels/include/gudhi/kernel.h delete mode 100644 src/Kernels/test/CMakeLists.txt delete mode 100644 src/Kernels/test/test_kernel.cpp create mode 100644 src/Persistence_representations/example/landscape.cpp create mode 100644 src/Persistence_representations/example/persistence_image.cpp create mode 100644 src/Persistence_representations/include/gudhi/Landscape.h create mode 100644 src/Persistence_representations/include/gudhi/Persistence_image.h create mode 100644 src/cython/cython/vectors.pyx diff --git a/src/Kernels/doc/COPYRIGHT b/src/Kernels/doc/COPYRIGHT deleted file mode 100644 index 0c36a526..00000000 --- a/src/Kernels/doc/COPYRIGHT +++ /dev/null @@ -1,19 +0,0 @@ -The files of this directory are part of the Gudhi Library. The Gudhi library -(Geometric Understanding in Higher Dimensions) is a generic C++ library for -computational topology. - -Author(s): Mathieu Carrière - -Copyright (C) 2017 INRIA - -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 . diff --git a/src/Kernels/doc/Intro_kernels.h b/src/Kernels/doc/Intro_kernels.h deleted file mode 100644 index 163690b1..00000000 --- a/src/Kernels/doc/Intro_kernels.h +++ /dev/null @@ -1,108 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2017 INRIA - * - * 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 DOC_KERNEL_INTRO_KERNEL_H_ -#define DOC_KERNEL_INTRO_KERNEL_H_ - -namespace Gudhi { - -namespace kernel { - -/** \defgroup kernel Kernels - * - * \author Mathieu Carrière - * - * @{ - * - * Kernels are generalized scalar products. They take the form of functions whose evaluations on pairs of persistence diagrams are equal - * to the scalar products of the images of the diagrams under some feature map into a (generally unknown and infinite dimensional) - * Hilbert space. Kernels are - * very useful to handle any type of data for algorithms that require at least a Hilbert structure, such as Principal Component Analysis - * or Support Vector Machines. In this package, we implement three kernels for persistence diagrams: - * the Persistence Scale Space Kernel (PSSK)---see \cite Reininghaus_Huber_ALL_PSSK, - * the Persistence Weighted Gaussian Kernel (PWGK)---see \cite Kusano_Fukumizu_Hiraoka_PWGK, - * and the Sliced Wasserstein Kernel (SWK)---see \cite pmlr-v70-carriere17a. - * - * \section pwg Persistence Weighted Gaussian Kernel and Persistence Scale Space Kernel - * - * The PWGK is built with Gaussian Kernel Mean Embedding, meaning that each persistence diagram is first - * sent to the Hilbert space of a Gaussian kernel with bandwidth parameter \f$\sigma >0\f$ using a weighted mean embedding \f$\Phi\f$: - * - * \f$ \Phi\,:\,D\,\rightarrow\,\sum_{p\in D}\,w(p)\,{\rm exp}\left(-\frac{\|p-\cdot\|_2^2}{2\sigma^2}\right) \f$, - * - * Usually, the weight function is chosen to be an arctan function of the distance of the point to the diagonal: - * \f$w(p) = {\rm arctan}(C\,|y-x|^\alpha)\f$, for some parameters \f$C,\alpha >0\f$. - * Then, either their scalar product in this space is - * computed (Linear Persistence Weighted Gaussian Kernel): - * - * \f$ LPWGK(D_1,D_2)=\langle\Phi(D_1),\Phi(D_2)\rangle - * \,=\,\sum_{p\in D_1}\,\sum_{q\in D_2}\,w(p)\,w(q)\,{\rm exp}\left(-\frac{\|p-q\|_2^2}{2\sigma^2}\right)\f$, - * - * or a second Gaussian kernel with bandwidth parameter \f$\tau >0\f$ is applied to their distance in this space - * (Gaussian Persistence Weighted Gaussian Kernel): - * - * \f$ GPWGK(D_1,D_2)={\rm exp}\left(-\frac{\|\Phi(D_1)-\Phi(D_2)\|^2}{2\tau^2} \right)\f$, - * where \f$\|\Phi(D_1)-\Phi(D_2)\|^2 = \langle\Phi(D_1)-\Phi(D_2),\Phi(D_1)-\Phi(D_2)\rangle\f$. - * - * It follows that the computation time is \f$O(n^2)\f$ where \f$n\f$ is the number of points - * in the diagrams. This time can be improved by computing approximations of the kernel - * with \f$m\f$ Fourier features \cite Rahimi07randomfeatures. In that case, the computation time becomes \f$O(mn)\f$. - * - * The PSSK is a Linear Persistence Weighted Gaussian Kernel between modified diagrams: - * the symmetric of each point with respect to the diagonal is first added in each diagram, and then the weight function - * is set to be +1 if the point is above the diagonal and -1 otherwise. - * - * \section sw Sliced Wasserstein Kernel - * - * The Sliced Wasserstein Kernel is defined as a Gaussian-like Kernel between persistence diagrams, where the distance used for - * comparison is the Sliced Wasserstein distance \f$SW\f$ between persistence diagrams, defined as the integral of the 1-norm - * between the sorted projections of the diagrams onto all lines passing through the origin: - * - * \f$ SW(D_1,D_2)=\int_{\theta\in\mathbb{S}}\,\|\pi_\theta(D_1\cup\pi_\Delta(D_2))-\pi_\theta(D_2\cup\pi_\Delta(D_1))\|_1{\rm d}\theta\f$, - * - * where \f$\pi_\theta\f$ is the projection onto the line defined with angle \f$\theta\f$ in the unit circle \f$\mathbb{S}\f$, - * and \f$\pi_\Delta\f$ is the projection onto the diagonal. - * The integral can be either computed exactly in \f$O(n^2{\rm log}(n))\f$ time, where \f$n\f$ is the number of points - * in the diagrams, or approximated by sampling \f$m\f$ lines in the circle in \f$O(mn{\rm log}(n))\f$ time. The SWK is then computed as: - * - * \f$ SWK(D_1,D_2) = {\rm exp}\left(-\frac{SW(D_1,D_2)}{2\sigma^2}\right).\f$ - * - * When launching: - * - * \code $> ./BasicEx ../../../../data/persistence_diagram/PD1 ../../../../data/persistence_diagram/PD2 - * \endcode - * - * the program output is: - * - * \include Kernels/kernel.txt - * - * - * \copyright GNU General Public License v3. - * \verbatim Contact: gudhi-users@lists.gforge.inria.fr \endverbatim - */ -/** @} */ // end defgroup kernel - -} // namespace kernel - -} // namespace Gudhi - -#endif // DOC_KERNEL_INTRO_KERNEL_H_ diff --git a/src/Kernels/example/CMakeLists.txt b/src/Kernels/example/CMakeLists.txt deleted file mode 100644 index d8ad4b42..00000000 --- a/src/Kernels/example/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 2.6) -project(Kernels_examples) - -add_executable ( BasicEx kernel_basic_example.cpp ) - -if (TBB_FOUND) - target_link_libraries(BasicEx ${TBB_LIBRARIES}) -endif() - -add_test(NAME Kernels_example_basicex COMMAND $ "${CMAKE_SOURCE_DIR}/data/persistence_diagram/PD1" "${CMAKE_SOURCE_DIR}/data/persistence_diagram/PD2") \ No newline at end of file diff --git a/src/Kernels/example/kernel.txt b/src/Kernels/example/kernel.txt deleted file mode 100644 index 5fb8b504..00000000 --- a/src/Kernels/example/kernel.txt +++ /dev/null @@ -1,8 +0,0 @@ -SWK exact = 0.875446 -SWK approx = 0.875204 -PSSK exact = 0.0218669 -PSSK approx = 0.0213766 -LPWGK exact = 2.57351 -LPWGK approx = 2.49102 -GPWGK exact = 0.98783 -GPWGK approx = 0.987591 \ No newline at end of file diff --git a/src/Kernels/example/kernel_basic_example.cpp b/src/Kernels/example/kernel_basic_example.cpp deleted file mode 100644 index 7ecbe401..00000000 --- a/src/Kernels/example/kernel_basic_example.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* This file is part of the Gudhi Library. The Gudhi library - * (Geometric Understanding in Higher Dimensions) is a generic C++ - * library for computational topology. - * - * Authors: Mathieu Carrière - * - * Copyright (C) 2017 INRIA - * - * 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 . - */ - -#include -#include -#include -#include -#include - - -void usage(int nbArgs, char *const progName) { - std::cerr << "Error: Number of arguments (" << nbArgs << ") is not correct\n"; - std::cerr << "Usage: " << progName << " PD1 PD2 \n"; - std::cerr << " i.e.: " << progName << " ../../../../data/persistence_diagram/PD1.pers ../../../../data/persistence_diagram/PD2.pers \n"; - exit(-1); // ----- >> -} - -int main(int argc, char **argv) { - - if (argc != 3) usage(argc, argv[0]); - - double sigma = 2; double tau = 5; - - std::string PDname1(argv[1]); std::string PDname2(argv[2]); - std::vector< std::pair > v1, v2; std::string line; double b,d; - - std::ifstream input1(PDname1); - while(std::getline(input1,line)){ - std::stringstream stream(line); stream >> b; stream >> d; v1.push_back(std::pair(b,d)); - } - - std::ifstream input2(PDname2); - while(std::getline(input2,line)){ - std::stringstream stream(line); stream >> b; stream >> d; v2.push_back(std::pair(b,d)); - } - - std::cout << "SWK exact = " << Gudhi::kernel::sliced_wasserstein_kernel (v1,v2,sigma,-1) << std::endl; - std::cout << "SWK approx = " << Gudhi::kernel::sliced_wasserstein_kernel (v1,v2,sigma) << std::endl; - std::cout << "PSSK exact = " << Gudhi::kernel::persistence_scale_space_kernel (v1,v2,sigma,-1) << std::endl; - std::cout << "PSSK approx = " << Gudhi::kernel::persistence_scale_space_kernel (v1,v2,sigma) << std::endl; - std::cout << "LPWGK exact = " << Gudhi::kernel::linear_persistence_weighted_gaussian_kernel (v1,v2,sigma,Gudhi::kernel::arctan_weight,-1) << std::endl; - std::cout << "LPWGK approx = " << Gudhi::kernel::linear_persistence_weighted_gaussian_kernel (v1,v2,sigma,Gudhi::kernel::arctan_weight) << std::endl; - std::cout << "GPWGK exact = " << Gudhi::kernel::gaussian_persistence_weighted_gaussian_kernel (v1,v2,sigma,tau,Gudhi::kernel::arctan_weight,-1) << std::endl; - std::cout << "GPWGK approx = " << Gudhi::kernel::gaussian_persistence_weighted_gaussian_kernel (v1,v2,sigma,tau,Gudhi::kernel::arctan_weight) << std::endl; - -} diff --git a/src/Kernels/include/gudhi/kernel.h b/src/Kernels/include/gudhi/kernel.h deleted file mode 100644 index 3293cc62..00000000 --- a/src/Kernels/include/gudhi/kernel.h +++ /dev/null @@ -1,365 +0,0 @@ -/* 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): Mathieu Carrière - * - * Copyright (C) 2018 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 KERNEL_H_ -#define KERNEL_H_ - -#include -#include -#include -#include -#include -#include //for numeric_limits<> -#include //for pair<> - -#include - - -namespace Gudhi { -namespace kernel { - -using PD = std::vector >; -double pi = boost::math::constants::pi(); - - - - -// ******************************************************************** -// Utils. -// ******************************************************************** - -bool sortAngle(const std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);} -bool myComp(const std::pair & P1, const std::pair & P2){return P1.second < P2.second;} - -double pss_weight(std::pair P){ - if(P.second > P.first) return 1; - else return -1; -} - -double arctan_weight(std::pair P){ - return atan(P.second - P.first); -} - -// Compute the angle formed by two points of a PD -double compute_angle(const PD & PersDiag, const int & i, const int & j){ - std::pair vect; double x1,y1, x2,y2; - x1 = PersDiag[i].first; y1 = PersDiag[i].second; - x2 = PersDiag[j].first; y2 = PersDiag[j].second; - if (y1 - y2 > 0){ - vect.first = y1 - y2; - vect.second = x2 - x1;} - else{ - if(y1 - y2 < 0){ - vect.first = y2 - y1; - vect.second = x1 - x2; - } - else{ - vect.first = 0; - vect.second = abs(x1 - x2);} - } - double norm = std::sqrt(vect.first*vect.first + vect.second*vect.second); - return asin(vect.second/norm); -} - -// Compute the integral of |cos()| between alpha and beta, valid only if alpha is in [-pi,pi] and beta-alpha is in [0,pi] -double compute_int_cos(const double & alpha, const double & beta){ - double res = 0; - if (alpha >= 0 && alpha <= pi){ - if (cos(alpha) >= 0){ - if(pi/2 <= beta){res = 2-sin(alpha)-sin(beta);} - else{res = sin(beta)-sin(alpha);} - } - else{ - if(1.5*pi <= beta){res = 2+sin(alpha)+sin(beta);} - else{res = sin(alpha)-sin(beta);} - } - } - if (alpha >= -pi && alpha <= 0){ - if (cos(alpha) <= 0){ - if(-pi/2 <= beta){res = 2+sin(alpha)+sin(beta);} - else{res = sin(alpha)-sin(beta);} - } - else{ - if(pi/2 <= beta){res = 2-sin(alpha)-sin(beta);} - else{res = sin(beta)-sin(alpha);} - } - } - return res; -} - -double compute_int(const double & theta1, const double & theta2, const int & p, const int & q, const PD & PD1, const PD & PD2){ - double norm = std::sqrt( (PD1[p].first-PD2[q].first)*(PD1[p].first-PD2[q].first) + (PD1[p].second-PD2[q].second)*(PD1[p].second-PD2[q].second) ); - double angle1; - if (PD1[p].first > PD2[q].first) - angle1 = theta1 - asin( (PD1[p].second-PD2[q].second)/norm ); - else - angle1 = theta1 - asin( (PD2[q].second-PD1[p].second)/norm ); - double angle2 = angle1 + theta2 - theta1; - double integral = compute_int_cos(angle1,angle2); - return norm*integral; -} - -template) > > -std::vector > Fourier_feat(PD D, std::vector > Z, Weight weight = arctan_weight){ - int m = D.size(); std::vector > B; int M = Z.size(); - for(int i = 0; i < M; i++){ - double d1 = 0; double d2 = 0; double zx = Z[i].first; double zy = Z[i].second; - for(int j = 0; j < m; j++){ - double x = D[j].first; double y = D[j].second; - d1 += weight(D[j])*cos(x*zx + y*zy); - d2 += weight(D[j])*sin(x*zx + y*zy); - } - B.emplace_back(d1,d2); - } - return B; -} - -std::vector > random_Fourier(double sigma, int M = 1000){ - std::normal_distribution distrib(0,1); std::vector > Z; std::random_device rd; - for(int i = 0; i < M; i++){ - std::mt19937 e1(rd()); std::mt19937 e2(rd()); - double zx = distrib(e1); double zy = distrib(e2); - Z.emplace_back(zx/sigma,zy/sigma); - } - return Z; -} - - - - - - - - - - -// ******************************************************************** -// Kernel computation. -// ******************************************************************** - - - - - -/** \brief Computes the Linear Persistence Weighted Gaussian Kernel between two persistence diagrams with random Fourier features. - * \ingroup kernel - * - * @param[in] PD1 first persistence diagram. - * @param[in] PD2 second persistence diagram. - * @param[in] sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. - * @param[in] weight weight function for the points in the diagrams. - * @param[in] M number of Fourier features (set -1 for exact computation). - * - */ -template) > > -double linear_persistence_weighted_gaussian_kernel(const PD & PD1, const PD & PD2, double sigma, Weight weight = arctan_weight, int M = 1000){ - - if(M == -1){ - int num_pts1 = PD1.size(); int num_pts2 = PD2.size(); double k = 0; - for(int i = 0; i < num_pts1; i++) - for(int j = 0; j < num_pts2; j++) - k += weight(PD1[i])*weight(PD2[j])*exp(-((PD1[i].first-PD2[j].first)*(PD1[i].first-PD2[j].first) + (PD1[i].second-PD2[j].second)*(PD1[i].second-PD2[j].second))/(2*sigma*sigma)); - return k; - } - else{ - std::vector > Z = random_Fourier(sigma, M); - std::vector > B1 = Fourier_feat(PD1,Z,weight); - std::vector > B2 = Fourier_feat(PD2,Z,weight); - double d = 0; for(int i = 0; i < M; i++) d += B1[i].first*B2[i].first + B1[i].second*B2[i].second; - return d/M; - } -} - -/** \brief Computes the Persistence Scale Space Kernel between two persistence diagrams with random Fourier features. - * \ingroup kernel - * - * @param[in] PD1 first persistence diagram. - * @param[in] PD2 second persistence diagram. - * @param[in] sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. - * @param[in] M number of Fourier features (set -1 for exact computation). - * - */ -double persistence_scale_space_kernel(const PD & PD1, const PD & PD2, double sigma, int M = 1000){ - PD pd1 = PD1; int numpts = PD1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(PD1[i].second,PD1[i].first); - PD pd2 = PD2; numpts = PD2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(PD2[i].second,PD2[i].first); - return linear_persistence_weighted_gaussian_kernel(pd1, pd2, 2*sqrt(sigma), pss_weight, M) / (2*8*pi*sigma); -} - - -/** \brief Computes the Gaussian Persistence Weighted Gaussian Kernel between two persistence diagrams with random Fourier features. - * \ingroup kernel - * - * @param[in] PD1 first persistence diagram. - * @param[in] PD2 second persistence diagram. - * @param[in] sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. - * @param[in] tau bandwidth parameter of the Gaussian Kernel used between the embeddings. - * @param[in] weight weight function for the points in the diagrams. - * @param[in] M number of Fourier features (set -1 for exact computation). - * - */ -template) > > -double gaussian_persistence_weighted_gaussian_kernel(const PD & PD1, const PD & PD2, double sigma, double tau, Weight weight = arctan_weight, int M = 1000){ - double k1 = linear_persistence_weighted_gaussian_kernel(PD1,PD1,sigma,weight,M); - double k2 = linear_persistence_weighted_gaussian_kernel(PD2,PD2,sigma,weight,M); - double k3 = linear_persistence_weighted_gaussian_kernel(PD1,PD2,sigma,weight,M); - return exp( - (k1+k2-2*k3) / (2*tau*tau) ); -} - - -/** \brief Computes the Sliced Wasserstein Kernel between two persistence diagrams with sampled directions. - * \ingroup kernel - * - * @param[in] PD1 first persistence diagram. - * @param[in] PD2 second persistence diagram. - * @param[in] sigma bandwidth parameter. - * @param[in] N number of points sampled on the circle (set -1 for exact computation). - * - */ -double sliced_wasserstein_kernel(PD PD1, PD PD2, double sigma, int N = 100){ - - if(N == -1){ - - // Add projections onto diagonal. - int n1, n2; n1 = PD1.size(); n2 = PD2.size(); double max_ordinate = std::numeric_limits::lowest(); - for (int i = 0; i < n2; i++){ - max_ordinate = std::max(max_ordinate, PD2[i].second); - PD1.emplace_back( (PD2[i].first+PD2[i].second)/2, (PD2[i].first+PD2[i].second)/2 ); - } - for (int i = 0; i < n1; i++){ - max_ordinate = std::max(max_ordinate, PD1[i].second); - PD2.emplace_back( (PD1[i].first+PD1[i].second)/2, (PD1[i].first+PD1[i].second)/2 ); - } - int num_pts_dgm = PD1.size(); - - // Slightly perturb the points so that the PDs are in generic positions. - int mag = 0; while(max_ordinate > 10){mag++; max_ordinate/=10;} - double thresh = pow(10,-5+mag); - srand(time(NULL)); - for (int i = 0; i < num_pts_dgm; i++){ - PD1[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); PD1[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); - PD2[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); PD2[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); - } - - // Compute all angles in both PDs. - std::vector > > angles1, angles2; - for (int i = 0; i < num_pts_dgm; i++){ - for (int j = i+1; j < num_pts_dgm; j++){ - double theta1 = compute_angle(PD1,i,j); double theta2 = compute_angle(PD2,i,j); - angles1.emplace_back(theta1, std::pair(i,j)); - angles2.emplace_back(theta2, std::pair(i,j)); - } - } - - // Sort angles. - std::sort(angles1.begin(), angles1.end(), sortAngle); std::sort(angles2.begin(), angles2.end(), sortAngle); - - // Initialize orders of the points of both PDs (given by ordinates when theta = -pi/2). - std::vector orderp1, orderp2; - for (int i = 0; i < num_pts_dgm; i++){ orderp1.push_back(i); orderp2.push_back(i); } - std::sort( orderp1.begin(), orderp1.end(), [=](int i, int j){ if(PD1[i].second != PD1[j].second) return (PD1[i].second < PD1[j].second); else return (PD1[i].first > PD1[j].first); } ); - std::sort( orderp2.begin(), orderp2.end(), [=](int i, int j){ if(PD2[i].second != PD2[j].second) return (PD2[i].second < PD2[j].second); else return (PD2[i].first > PD2[j].first); } ); - - // Find the inverses of the orders. - std::vector order1(num_pts_dgm); std::vector order2(num_pts_dgm); - for(int i = 0; i < num_pts_dgm; i++) for (int j = 0; j < num_pts_dgm; j++) if(orderp1[j] == i){ order1[i] = j; break; } - for(int i = 0; i < num_pts_dgm; i++) for (int j = 0; j < num_pts_dgm; j++) if(orderp2[j] == i){ order2[i] = j; break; } - - // Record all inversions of points in the orders as theta varies along the positive half-disk. - std::vector > > anglePerm1(num_pts_dgm); - std::vector > > anglePerm2(num_pts_dgm); - - int M1 = angles1.size(); - for (int i = 0; i < M1; i++){ - double theta = angles1[i].first; int p = angles1[i].second.first; int q = angles1[i].second.second; - anglePerm1[order1[p]].emplace_back(p,theta); - anglePerm1[order1[q]].emplace_back(q,theta); - int a = order1[p]; int b = order1[q]; order1[p] = b; order1[q] = a; - } - - int M2 = angles2.size(); - for (int i = 0; i < M2; i++){ - double theta = angles2[i].first; int p = angles2[i].second.first; int q = angles2[i].second.second; - anglePerm2[order2[p]].emplace_back(p,theta); - anglePerm2[order2[q]].emplace_back(q,theta); - int a = order2[p]; int b = order2[q]; order2[p] = b; order2[q] = a; - } - - for (int i = 0; i < num_pts_dgm; i++){ - anglePerm1[order1[i]].emplace_back(i,pi/2); - anglePerm2[order2[i]].emplace_back(i,pi/2); - } - - // Compute the SW distance with the list of inversions. - double sw = 0; - for (int i = 0; i < num_pts_dgm; i++){ - std::vector > U,V; U = anglePerm1[i]; V = anglePerm2[i]; - double theta1, theta2; theta1 = -pi/2; - unsigned int ku, kv; ku = 0; kv = 0; theta2 = std::min(U[ku].second,V[kv].second); - while(theta1 != pi/2){ - if(PD1[U[ku].first].first != PD2[V[kv].first].first || PD1[U[ku].first].second != PD2[V[kv].first].second) - if(theta1 != theta2) - sw += compute_int(theta1, theta2, U[ku].first, V[kv].first, PD1, PD2); - theta1 = theta2; - if ( (theta2 == U[ku].second) && ku < U.size()-1 ) ku++; - if ( (theta2 == V[kv].second) && kv < V.size()-1 ) kv++; - theta2 = std::min(U[ku].second, V[kv].second); - } - } - - return exp( -(sw/pi)/(2*sigma*sigma) ); - - } - - - else{ - double step = pi/N; double sw = 0; - - // Add projections onto diagonal. - int n1, n2; n1 = PD1.size(); n2 = PD2.size(); - for (int i = 0; i < n2; i++) - PD1.emplace_back( (PD2[i].first + PD2[i].second)/2, (PD2[i].first + PD2[i].second)/2 ); - for (int i = 0; i < n1; i++) - PD2.emplace_back( (PD1[i].first + PD1[i].second)/2, (PD1[i].first + PD1[i].second)/2 ); - int n = PD1.size(); - - // Sort and compare all projections. - //#pragma omp parallel for - for (int i = 0; i < N; i++){ - std::vector > L1, L2; - for (int j = 0; j < n; j++){ - L1.emplace_back( j, PD1[j].first*cos(-pi/2+i*step) + PD1[j].second*sin(-pi/2+i*step) ); - L2.emplace_back( j, PD2[j].first*cos(-pi/2+i*step) + PD2[j].second*sin(-pi/2+i*step) ); - } - std::sort(L1.begin(),L1.end(), myComp); std::sort(L2.begin(),L2.end(), myComp); - double f = 0; for (int j = 0; j < n; j++) f += std::abs(L1[j].second - L2[j].second); - sw += f*step; - } - return exp( -(sw/pi)/(2*sigma*sigma) ); - } -} - - -} // namespace kernel - -} // namespace Gudhi - -#endif //KERNEL_H_ diff --git a/src/Kernels/test/CMakeLists.txt b/src/Kernels/test/CMakeLists.txt deleted file mode 100644 index 95c72a7f..00000000 --- a/src/Kernels/test/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -cmake_minimum_required(VERSION 2.6) -project(kernel_tests) - -include(GUDHI_test_coverage) - -add_executable ( kernel_test_unit test_kernel.cpp ) -target_link_libraries(kernel_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) -if (TBB_FOUND) - target_link_libraries(kernel_test_unit ${TBB_LIBRARIES}) -endif() - -gudhi_add_coverage_test(kernel_test_unit) diff --git a/src/Kernels/test/test_kernel.cpp b/src/Kernels/test/test_kernel.cpp deleted file mode 100644 index db05fd28..00000000 --- a/src/Kernels/test/test_kernel.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* 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): Mathieu Carrière - * - * Copyright (C) 2017 INRIA - * - * 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 . - */ - -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE "kernel" - -#include -#include // float comparison -#include -#include -#include -#include // std::max -#include -#include -#include - -BOOST_AUTO_TEST_CASE(check_PSS) { - std::vector< std::pair > v1, v2; - v1.emplace_back(std::pair(0,1)); - v2.emplace_back(std::pair(0,2)); - BOOST_CHECK(std::abs(Gudhi::kernel::pssk(v1,v2,1) - Gudhi::kernel::approx_pssk(v1,v2,1)) <= 1e-1); -} - -BOOST_AUTO_TEST_CASE(check_PWG) { - std::vector< std::pair > v1, v2; - v1.emplace_back(std::pair(0,1)); - v2.emplace_back(std::pair(0,2)); - BOOST_CHECK(std::abs(Gudhi::kernel::lpwgk(v1,v2,1) - Gudhi::kernel::approx_lpwgk(v1,v2,1)) <= 1e-1); - BOOST_CHECK(std::abs(Gudhi::kernel::gpwgk(v1,v2,1,1) - Gudhi::kernel::approx_gpwgk(v1,v2,1,1)) <= 1e-1); -} - -BOOST_AUTO_TEST_CASE(check_SW) { - std::vector< std::pair > v1, v2; - v2.emplace_back(std::pair(0,2)); - BOOST_CHECK(std::abs(Gudhi::kernel::sw(v1,v2) - Gudhi::kernel::approx_sw(v1,v2)) <= 1e-3); - BOOST_CHECK(std::abs(Gudhi::kernel::sw(v1,v2) - 2*std::sqrt(2)/3.1415) <= 1e-3); -} diff --git a/src/Persistence_representations/example/CMakeLists.txt b/src/Persistence_representations/example/CMakeLists.txt index 79d39c4d..89284e38 100644 --- a/src/Persistence_representations/example/CMakeLists.txt +++ b/src/Persistence_representations/example/CMakeLists.txt @@ -37,3 +37,12 @@ add_test(NAME Persistence_weighted_gaussian COMMAND $) install(TARGETS Persistence_weighted_gaussian DESTINATION bin) +add_executable ( Persistence_image persistence_image.cpp ) +add_test(NAME Persistence_image + COMMAND $) +install(TARGETS Persistence_image DESTINATION bin) + +add_executable ( Landscape landscape.cpp ) +add_test(NAME Landscape + COMMAND $) +install(TARGETS Landscape DESTINATION bin) diff --git a/src/Persistence_representations/example/landscape.cpp b/src/Persistence_representations/example/landscape.cpp new file mode 100644 index 00000000..5fa84a7c --- /dev/null +++ b/src/Persistence_representations/example/landscape.cpp @@ -0,0 +1,51 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 . + */ + +#include + +#include +#include +#include + +using LS = Gudhi::Persistence_representations::Landscape; + +int main(int argc, char** argv) { + + std::vector > persistence; + + persistence.push_back(std::make_pair(1, 2)); + persistence.push_back(std::make_pair(6, 8)); + persistence.push_back(std::make_pair(0, 4)); + persistence.push_back(std::make_pair(3, 8)); + + int nb_ls = 3; double min_x = 0.0; double max_x = 10.0; int res_x = 100; + + LS ls(persistence, nb_ls, min_x, max_x, res_x); + std::vector > L = ls.vectorize(); + + for(int i = 0; i < nb_ls; i++){ + for(int j = 0; j < res_x; j++) std::cout << L[i][j] << " "; + std::cout << std::endl; + } + + return 0; +} diff --git a/src/Persistence_representations/example/persistence_image.cpp b/src/Persistence_representations/example/persistence_image.cpp new file mode 100644 index 00000000..dfa469d4 --- /dev/null +++ b/src/Persistence_representations/example/persistence_image.cpp @@ -0,0 +1,54 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 . + */ + +#include +#include + +#include +#include +#include +#include + +using PI = Gudhi::Persistence_representations::Persistence_image; +using Weight = std::function) >; + +int main(int argc, char** argv) { + + std::vector > persistence; + + persistence.push_back(std::make_pair(1, 2)); + persistence.push_back(std::make_pair(6, 8)); + persistence.push_back(std::make_pair(0, 4)); + persistence.push_back(std::make_pair(3, 8)); + + double min_x = 0.0; double max_x = 10.0; int res_x = 100; double min_y = 0.0; double max_y = 10.0; int res_y = 100; double sigma = 1.0; Weight weight = Gudhi::Persistence_representations::Persistence_weighted_gaussian::linear_weight; + + PI pim(persistence, min_x, max_x, res_x, min_y, max_y, res_y, weight, sigma); + std::vector > P = pim.vectorize(); + + for(int i = 0; i < res_y; i++){ + for(int j = 0; j < res_x; j++) std::cout << P[i][j] << " "; + std::cout << std::endl; + } + + return 0; +} diff --git a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp index dea5dab6..234f6323 100644 --- a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp +++ b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp @@ -48,11 +48,11 @@ int main(int argc, char** argv) { double tau = 1; int m = 10000; - PWG PWG1(persistence1, sigma, m, PWG::arctan_weight); - PWG PWG2(persistence2, sigma, m, PWG::arctan_weight); + PWG PWG1(persistence1, sigma, m, PWG::arctan_weight(1,1)); + PWG PWG2(persistence2, sigma, m, PWG::arctan_weight(1,1)); - PWG PWGex1(persistence1, sigma, -1, PWG::arctan_weight); - PWG PWGex2(persistence2, sigma, -1, PWG::arctan_weight); + PWG PWGex1(persistence1, sigma, -1, PWG::arctan_weight(1,1)); + PWG PWGex2(persistence2, sigma, -1, PWG::arctan_weight(1,1)); // Linear PWG diff --git a/src/Persistence_representations/include/gudhi/Landscape.h b/src/Persistence_representations/include/gudhi/Landscape.h new file mode 100644 index 00000000..d6608a57 --- /dev/null +++ b/src/Persistence_representations/include/gudhi/Landscape.h @@ -0,0 +1,103 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 LANDSCAPE_H_ +#define LANDSCAPE_H_ + +// gudhi include +#include +#include +#include + +// standard include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using PD = std::vector >; + +namespace Gudhi { +namespace Persistence_representations { + +/** + * \class Landscape gudhi/Landscape.h + * \brief A class implementing the Landscapes. + * + * \ingroup Persistence_representations + * + * \details + * +**/ + +class Landscape { + + protected: + PD diagram; + int res_x, nb_ls; + double min_x, max_x; + + public: + + /** \brief Landscape constructor. + * \ingroup Landscape + * + */ + Landscape(PD _diagram, int _nb_ls = 5, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10){diagram = _diagram; nb_ls = _nb_ls; min_x = _min_x; max_x = _max_x; res_x = _res_x;} + + /** \brief Computes the landscape of a diagram. + * \ingroup Landscape + * + */ + std::vector > vectorize() { + std::vector > ls; for(int i = 0; i < nb_ls; i++) ls.emplace_back(); + int num_pts = diagram.size(); double step = (max_x - min_x)/res_x; + + for(int i = 0; i < res_x; i++){ + double x = min_x + i*step; double t = x / std::sqrt(2); std::vector events; + for(int j = 0; j < num_pts; j++){ + double px = diagram[j].first; double py = diagram[j].second; + if(t >= px && t <= py){ if(t >= (px+py)/2) events.push_back(std::sqrt(2)*(py-t)); else events.push_back(std::sqrt(2)*(t-px)); } + } + + std::sort(events.begin(), events.end(), [](const double & a, const double & b){return a > b;}); int nb_events = events.size(); + for (int j = 0; j < nb_ls; j++){ if(j < nb_events) ls[j].push_back(events[j]); else ls[j].push_back(0); } + } + + return ls; + } + + + + +}; + +} // namespace Landscape +} // namespace Gudhi + +#endif // LANDSCAPE_H_ diff --git a/src/Persistence_representations/include/gudhi/Persistence_image.h b/src/Persistence_representations/include/gudhi/Persistence_image.h new file mode 100644 index 00000000..6c9f75b7 --- /dev/null +++ b/src/Persistence_representations/include/gudhi/Persistence_image.h @@ -0,0 +1,117 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 PERSISTENCE_IMAGE_H_ +#define PERSISTENCE_IMAGE_H_ + +// gudhi include +#include +#include +#include +#include + +// standard include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using PD = std::vector >; +using Weight = std::function) >; + +namespace Gudhi { +namespace Persistence_representations { + +/** + * \class Persistence_image gudhi/Persistence_image.h + * \brief A class implementing the Persistence Images. + * + * \ingroup Persistence_representations + * + * \details + * +**/ + +class Persistence_image { + + protected: + PD diagram; + int res_x, res_y; + double min_x, max_x, min_y, max_y; + Weight weight; + double sigma; + + public: + + /** \brief Persistence Image constructor. + * \ingroup Persistence_image + * + */ + Persistence_image(PD _diagram, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10, double _min_y = 0.0, double _max_y = 1.0, int _res_y = 10, + Weight _weight = Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight(1,1), double _sigma = 1.0){ + diagram = _diagram; min_x = _min_x; max_x = _max_x; res_x = _res_x; min_y = _min_y; max_y = _max_y; res_y = _res_y, weight = _weight; sigma = _sigma; + } + + /** \brief Computes the persistence image of a diagram. + * \ingroup Persistence_image + * + */ + std::vector > vectorize() { + std::vector > im; for(int i = 0; i < res_y; i++) im.emplace_back(); + double step_x = (max_x - min_x)/res_x; double step_y = (max_y - min_y)/res_y; + + int num_pts = diagram.size(); + + for(int i = 0; i < res_y; i++){ + double y = min_y + i*step_y; + for(int j = 0; j < res_x; j++){ + double x = min_x + j*step_x; + + double pixel_value = 0; + for(int k = 0; k < num_pts; k++){ + double px = diagram[k].first; double py = diagram[k].second; + pixel_value += weight(std::pair(px,py)) * std::exp( -((x-px)*(x-px) + (y-(py-px))*(y-(py-px))) / (2*sigma*sigma) ) / (sigma*std::sqrt(2*pi)); + } + im[i].push_back(pixel_value); + + } + } + + return im; + + } + + + + +}; + +} // namespace Persistence_image +} // namespace Gudhi + +#endif // PERSISTENCE_IMAGE_H_ diff --git a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h index b30e0273..9a63fccd 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h +++ b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h @@ -97,7 +97,7 @@ class Persistence_weighted_gaussian{ * @param[in] _weight weight function for the points in the diagrams. * */ - Persistence_weighted_gaussian(PD _diagram, double _sigma = 1.0, int _approx = 1000, Weight _weight = arctan_weight){diagram = _diagram; sigma = _sigma; approx = _approx; weight = _weight;} + Persistence_weighted_gaussian(PD _diagram, double _sigma = 1.0, int _approx = 1000, Weight _weight = arctan_weight(1,1)){diagram = _diagram; sigma = _sigma; approx = _approx; weight = _weight;} PD get_diagram() const {return this->diagram;} double get_sigma() const {return this->sigma;} @@ -115,16 +115,13 @@ class Persistence_weighted_gaussian{ * @param[in] p point in 2D. * */ - static double pss_weight(std::pair p){ - if(p.second > p.first) return 1; - else return -1; - } + static double pss_weight(std::pair p) {if(p.second > p.first) return 1; else return -1;} + static double linear_weight(std::pair p) {return std::abs(p.second - p.first);} + static double const_weight(std::pair p) {return 1;} + static std::function) > arctan_weight(double C, double power) {return [=](std::pair p){return C * atan(std::pow(std::abs(p.second - p.first), power));};} - static double arctan_weight(std::pair p){ - return atan(p.second - p.first); - } - std::vector > Fourier_feat(PD diag, std::vector > z, Weight weight = arctan_weight){ + std::vector > Fourier_feat(PD diag, std::vector > z, Weight weight = arctan_weight(1,1)){ int md = diag.size(); std::vector > b; int mz = z.size(); for(int i = 0; i < mz; i++){ double d1 = 0; double d2 = 0; double zx = z[i].first; double zy = z[i].second; diff --git a/src/cython/cython/kernels.pyx b/src/cython/cython/kernels.pyx index 466917b1..0cb296ec 100644 --- a/src/cython/cython/kernels.pyx +++ b/src/cython/cython/kernels.pyx @@ -34,8 +34,8 @@ cdef extern from "Kernels_interface.h" namespace "Gudhi::persistence_diagram": vector[vector[double]] sw_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) double pss (vector[pair[double, double]], vector[pair[double, double]], double, int) vector[vector[double]] pss_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) - double pwg (vector[pair[double, double]], vector[pair[double, double]], double, int) - vector[vector[double]] pwg_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) + double pwg (vector[pair[double, double]], vector[pair[double, double]], double, int, double, double) + vector[vector[double]] pwg_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int, double, double) def sliced_wasserstein(diagram_1, diagram_2, sigma = 1, N = 100): """ @@ -65,7 +65,7 @@ def sliced_wasserstein_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100): """ return sw_matrix(diagrams_1, diagrams_2, sigma, N) -def persistence_weighted_gaussian(diagram_1, diagram_2, sigma = 1, N = 100): +def persistence_weighted_gaussian(diagram_1, diagram_2, sigma = 1, N = 100, C = 1, p = 1): """ :param diagram_1: The first diagram. @@ -74,12 +74,14 @@ def persistence_weighted_gaussian(diagram_1, diagram_2, sigma = 1, N = 100): :type diagram_2: vector[pair[double, double]] :param sigma: bandwidth of Gaussian :param N: number of Fourier features + :param C: cost of persistence weight + :param p: power of persistence weight :returns: the persistence weighted gaussian kernel. """ - return pwg(diagram_1, diagram_2, sigma, N) + return pwg(diagram_1, diagram_2, sigma, N, C, p) -def persistence_weighted_gaussian_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100): +def persistence_weighted_gaussian_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100, C = 1, p = 1): """ :param diagram_1: The first set of diagrams. @@ -88,10 +90,12 @@ def persistence_weighted_gaussian_matrix(diagrams_1, diagrams_2, sigma = 1, N = :type diagram_2: vector[vector[pair[double, double]]] :param sigma: bandwidth of Gaussian :param N: number of Fourier features + :param C: cost of persistence weight + :param p: power of persistence weight :returns: the persistence weighted gaussian kernel matrix. """ - return pwg_matrix(diagrams_1, diagrams_2, sigma, N) + return pwg_matrix(diagrams_1, diagrams_2, sigma, N, C, p) def persistence_scale_space(diagram_1, diagram_2, sigma = 1, N = 100): """ diff --git a/src/cython/cython/vectors.pyx b/src/cython/cython/vectors.pyx new file mode 100644 index 00000000..42390ae6 --- /dev/null +++ b/src/cython/cython/vectors.pyx @@ -0,0 +1,65 @@ +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.utility cimport pair +import os + +"""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): Mathieu Carriere + + Copyright (C) 2018 INRIA + + 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 . +""" + +__author__ = "Mathieu Carriere" +__copyright__ = "Copyright (C) 2018 INRIA" +__license__ = "GPL v3" + +cdef extern from "Vectors_interface.h" namespace "Gudhi::persistence_diagram": + vector[vector[double]] compute_ls (vector[pair[double, double]], int, double, double, int) + vector[vector[double]] compute_pim (vector[pair[double, double]], double, double, int, double, double, int, string, double, double, double) + +def landscape(diagram, nb_ls = 10, min_x = 0.0, max_x = 1.0, res_x = 100): + """ + + :param diagram: The diagram + :type diagram: vector[pair[double, double]] + :param nb_ls: Number of landscapes + :param min_x: Minimum abscissa + :param max_x: Maximum abscissa + :param res_x: Number of samples + + :returns: the landscape + """ + return compute_ls(diagram, nb_ls, min_x, max_x, res_x) + +def persistence_image(diagram, min_x = 0.0, max_x = 1.0, res_x = 10, min_y = 0.0, max_y = 1.0, res_y = 10, weight = "linear", sigma = 1.0, C = 1.0, p = 1.0): + """ + + :param diagram: The diagram + :type diagram: vector[vector[pair[double, double]]] + :param min_x: Minimum abscissa + :param max_x: Maximum abscissa + :param res_x: Number of abscissa pixels + :param min_x: Minimum ordinate + :param max_x: Maximum ordinate + :param res_x: Number of ordinate pixels + :param sigma: bandwidth of Gaussian + + :returns: the persistence image + """ + return compute_pim(diagram, min_x, max_x, res_x, min_y, max_y, res_y, weight, sigma, C, p) diff --git a/src/cython/gudhi.pyx.in b/src/cython/gudhi.pyx.in index 7f42968d..3ff68085 100644 --- a/src/cython/gudhi.pyx.in +++ b/src/cython/gudhi.pyx.in @@ -37,6 +37,7 @@ include '@CMAKE_CURRENT_SOURCE_DIR@/cython/reader_utils.pyx' include '@CMAKE_CURRENT_SOURCE_DIR@/cython/witness_complex.pyx' include '@CMAKE_CURRENT_SOURCE_DIR@/cython/strong_witness_complex.pyx' include '@CMAKE_CURRENT_SOURCE_DIR@/cython/kernels.pyx' +include '@CMAKE_CURRENT_SOURCE_DIR@/cython/vectors.pyx' @GUDHI_CYTHON_ALPHA_COMPLEX@ @GUDHI_CYTHON_EUCLIDEAN_WITNESS_COMPLEX@ @GUDHI_CYTHON_SUBSAMPLING@ diff --git a/src/cython/include/Kernels_interface.h b/src/cython/include/Kernels_interface.h index 1742d016..0da28245 100644 --- a/src/cython/include/Kernels_interface.h +++ b/src/cython/include/Kernels_interface.h @@ -34,25 +34,24 @@ namespace Gudhi { namespace persistence_diagram { - double sw(const std::vector>& diag1, - const std::vector>& diag2, - double sigma, int N) { + + // ******************* + // Kernel evaluations. + // ******************* + + double sw(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { Gudhi::Persistence_representations::Sliced_Wasserstein sw1(diag1, sigma, N); Gudhi::Persistence_representations::Sliced_Wasserstein sw2(diag2, sigma, N); return sw1.compute_scalar_product(sw2); } - double pwg(const std::vector>& diag1, - const std::vector>& diag2, - double sigma, int N) { - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, sigma, N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, sigma, N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight); + double pwg(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N, double C, double p) { + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, sigma, N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight(C,p)); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, sigma, N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight(C,p)); return pwg1.compute_scalar_product(pwg2); } - double pss(const std::vector>& diag1, - const std::vector>& diag2, - double sigma, int N) { + double pss(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { std::vector> pd1 = diag1; int numpts = diag1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(diag1[i].second,diag1[i].first); std::vector> pd2 = diag2; numpts = diag2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(diag2[i].second,diag2[i].first); @@ -62,9 +61,7 @@ namespace persistence_diagram { return pwg1.compute_scalar_product (pwg2) / (16*Gudhi::Persistence_representations::pi*sigma); } - double pss_sym(const std::vector>& diag1, - const std::vector>& diag2, - double sigma, int N) { + double pss_sym(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); @@ -72,9 +69,11 @@ namespace persistence_diagram { } - std::vector > sw_matrix(const std::vector > >& s1, - const std::vector > >& s2, - double sigma, int N){ + // **************** + // Kernel matrices. + // **************** + + std::vector > sw_matrix(const std::vector > >& s1, const std::vector > >& s2, double sigma, int N){ std::vector > matrix; std::vector ss1; int num_diag_1 = s1.size(); for(int i = 0; i < num_diag_1; i++){Gudhi::Persistence_representations::Sliced_Wasserstein sw1(s1[i], sigma, N); ss1.push_back(sw1);} @@ -87,22 +86,17 @@ namespace persistence_diagram { return matrix; } - std::vector > pwg_matrix(const std::vector > >& s1, - const std::vector > >& s2, - double sigma, int N){ + std::vector > pwg_matrix(const std::vector > >& s1, const std::vector > >& s2, double sigma, int N, double C, double p){ std::vector > matrix; int num_diag_1 = s1.size(); int num_diag_2 = s2.size(); for(int i = 0; i < num_diag_1; i++){ std::cout << 100.0*i/num_diag_1 << " %" << std::endl; - std::vector ps; for(int j = 0; j < num_diag_2; j++) ps.push_back(pwg(s1[i], s2[j], sigma, N)); matrix.push_back(ps); + std::vector ps; for(int j = 0; j < num_diag_2; j++) ps.push_back(pwg(s1[i], s2[j], sigma, N, C, p)); matrix.push_back(ps); } return matrix; } - std::vector > pss_matrix(const std::vector > >& s1, - const std::vector > >& s2, - double sigma, int N){ - std::vector > > ss1, ss2; - std::vector > matrix; int num_diag_1 = s1.size(); int num_diag_2 = s2.size(); + std::vector > pss_matrix(const std::vector > >& s1, const std::vector > >& s2, double sigma, int N){ + std::vector > > ss1, ss2; std::vector > matrix; int num_diag_1 = s1.size(); int num_diag_2 = s2.size(); for(int i = 0; i < num_diag_1; i++){ std::vector> pd1 = s1[i]; int numpts = s1[i].size(); for(int j = 0; j < numpts; j++) pd1.emplace_back(s1[i][j].second,s1[i][j].first); -- cgit v1.2.3 From 5e24206f945f66575c7c179d74e9661cf60ca3df Mon Sep 17 00:00:00 2001 From: mcarrier Date: Fri, 20 Apr 2018 15:56:37 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3386 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 29ea554ecd104927cce84dbc21dab531fc21265c --- src/cython/include/Vectors_interface.h | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/cython/include/Vectors_interface.h diff --git a/src/cython/include/Vectors_interface.h b/src/cython/include/Vectors_interface.h new file mode 100644 index 00000000..49d28e7c --- /dev/null +++ b/src/cython/include/Vectors_interface.h @@ -0,0 +1,59 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 INRIA + * + * 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 INCLUDE_VECTORS_INTERFACE_H_ +#define INCLUDE_VECTORS_INTERFACE_H_ + +#include +#include +#include + +#include +#include +#include // for std::pair + +using Weight = std::function) >; + +namespace Gudhi { + +namespace persistence_diagram { + + std::vector > compute_ls(const std::vector >& diag, int nb_ls, double min_x, double max_x, int res_x) { + Gudhi::Persistence_representations::Landscape L(diag, nb_ls, min_x, max_x, res_x); + return L.vectorize(); + } + + std::vector > compute_pim(const std::vector >& diag, double min_x, double max_x, int res_x, double min_y, double max_y, int res_y, std::string weight, double sigma, double C, double p) { + Weight weight_fn; + if(weight.compare("linear") == 0) weight_fn = Gudhi::Persistence_representations::Persistence_weighted_gaussian::linear_weight; + if(weight.compare("arctan") == 0) weight_fn = Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight(C,p); + if(weight.compare("const") == 0) weight_fn = Gudhi::Persistence_representations::Persistence_weighted_gaussian::const_weight; + Gudhi::Persistence_representations::Persistence_image P(diag, min_x, max_x, res_x, min_y, max_y, res_y, weight_fn, sigma); + return P.vectorize(); + } + +} // namespace persistence_diagram + +} // namespace Gudhi + + +#endif // INCLUDE_VECTORS_INTERFACE_H_ -- cgit v1.2.3 From 541284f6f1bf7d4a76daac8a52850c7162a765cb Mon Sep 17 00:00:00 2001 From: mcarrier Date: Mon, 23 Apr 2018 15:22:13 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3387 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 3fe2ae4af0c7cadf507fc5148c05dcf664c5e151 --- .../doc/Persistence_representations_doc.h | 5 +- .../example/persistence_image.cpp | 2 +- .../example/persistence_weighted_gaussian.cpp | 24 +- .../example/sliced_wasserstein.cpp | 3 +- .../include/gudhi/Landscape.h | 87 ++++---- .../include/gudhi/Persistence_image.h | 41 ++-- .../include/gudhi/Persistence_weighted_gaussian.h | 70 +++--- .../include/gudhi/Sliced_Wasserstein.h | 243 ++++++++++----------- .../gudhi/common_persistence_representations.h | 22 +- 9 files changed, 242 insertions(+), 255 deletions(-) diff --git a/src/Persistence_representations/doc/Persistence_representations_doc.h b/src/Persistence_representations/doc/Persistence_representations_doc.h index 6d4cc96c..ca283017 100644 --- a/src/Persistence_representations/doc/Persistence_representations_doc.h +++ b/src/Persistence_representations/doc/Persistence_representations_doc.h @@ -24,7 +24,6 @@ #define DOC_GUDHI_STAT_H_ namespace Gudhi { - namespace Persistence_representations { /** \defgroup Persistence_representations Persistence representations @@ -254,11 +253,11 @@ namespace Persistence_representations { -\section sec_persistence_kernels Kernels on Persistence Diagrams +\section sec_persistence_kernels Kernels on persistence diagrams Reference manual: \ref Gudhi::Persistence_representations::Sliced_Wasserstein
Reference manual: \ref Gudhi::Persistence_representations::Persistence_weighted_gaussian
- Kernels for Persistence Diagrams can be regarded as infinite-dimensional vectorizations. More specifically, + Kernels for persistence diagrams can be regarded as infinite-dimensional vectorizations. More specifically, they are similarity functions whose evaluations on pairs of persistence diagrams equals the scalar products between images of these pairs under a map \f$\Phi\f$ taking values in a specific (possibly non Euclidean) Hilbert space \f$k(D_i, D_j) = \langle \Phi(D_i),\Phi(D_j)\rangle\f$. Reciprocally, classical results of learning theory ensure that such a \f$\Phi\f$ exists for a given similarity function \f$k\f$ if and only if \f$k\f$ is positive semi-definite. diff --git a/src/Persistence_representations/example/persistence_image.cpp b/src/Persistence_representations/example/persistence_image.cpp index dfa469d4..cdce3bbf 100644 --- a/src/Persistence_representations/example/persistence_image.cpp +++ b/src/Persistence_representations/example/persistence_image.cpp @@ -40,7 +40,7 @@ int main(int argc, char** argv) { persistence.push_back(std::make_pair(0, 4)); persistence.push_back(std::make_pair(3, 8)); - double min_x = 0.0; double max_x = 10.0; int res_x = 100; double min_y = 0.0; double max_y = 10.0; int res_y = 100; double sigma = 1.0; Weight weight = Gudhi::Persistence_representations::Persistence_weighted_gaussian::linear_weight; + double min_x = 0.0; double max_x = 10.0; int res_x = 100; double min_y = 0.0; double max_y = 10.0; int res_y = 100; double sigma = 1.0; Weight weight = Gudhi::Persistence_representations::linear_weight; PI pim(persistence, min_x, max_x, res_x, min_y, max_y, res_y, weight, sigma); std::vector > P = pim.vectorize(); diff --git a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp index 234f6323..db60755f 100644 --- a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp +++ b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp @@ -26,13 +26,11 @@ #include #include -using PD = std::vector >; using PWG = Gudhi::Persistence_representations::Persistence_weighted_gaussian; int main(int argc, char** argv) { - std::vector > persistence1; - std::vector > persistence2; + Persistence_diagram persistence1, persistence2; persistence1.push_back(std::make_pair(1, 2)); persistence1.push_back(std::make_pair(6, 8)); @@ -48,11 +46,11 @@ int main(int argc, char** argv) { double tau = 1; int m = 10000; - PWG PWG1(persistence1, sigma, m, PWG::arctan_weight(1,1)); - PWG PWG2(persistence2, sigma, m, PWG::arctan_weight(1,1)); + PWG PWG1(persistence1, sigma, m, Gudhi::Persistence_representations::arctan_weight(1,1)); + PWG PWG2(persistence2, sigma, m, Gudhi::Persistence_representations::arctan_weight(1,1)); - PWG PWGex1(persistence1, sigma, -1, PWG::arctan_weight(1,1)); - PWG PWGex2(persistence2, sigma, -1, PWG::arctan_weight(1,1)); + PWG PWGex1(persistence1, sigma, -1, Gudhi::Persistence_representations::arctan_weight(1,1)); + PWG PWGex2(persistence2, sigma, -1, Gudhi::Persistence_representations::arctan_weight(1,1)); // Linear PWG @@ -82,14 +80,14 @@ int main(int argc, char** argv) { // PSS - PD pd1 = persistence1; int numpts = persistence1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(persistence1[i].second,persistence1[i].first); - PD pd2 = persistence2; numpts = persistence2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(persistence2[i].second,persistence2[i].first); + Persistence_diagram pd1 = persistence1; int numpts = persistence1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(persistence1[i].second,persistence1[i].first); + Persistence_diagram pd2 = persistence2; numpts = persistence2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(persistence2[i].second,persistence2[i].first); - PWG pwg1(pd1, 2*std::sqrt(sigma), m, PWG::pss_weight); - PWG pwg2(pd2, 2*std::sqrt(sigma), m, PWG::pss_weight); + PWG pwg1(pd1, 2*std::sqrt(sigma), m, Gudhi::Persistence_representations::pss_weight); + PWG pwg2(pd2, 2*std::sqrt(sigma), m, Gudhi::Persistence_representations::pss_weight); - PWG pwgex1(pd1, 2*std::sqrt(sigma), -1, PWG::pss_weight); - PWG pwgex2(pd2, 2*std::sqrt(sigma), -1, PWG::pss_weight); + PWG pwgex1(pd1, 2*std::sqrt(sigma), -1, Gudhi::Persistence_representations::pss_weight); + PWG pwgex2(pd2, 2*std::sqrt(sigma), -1, Gudhi::Persistence_representations::pss_weight); std::cout << "Approx PSS kernel: " << pwg1.compute_scalar_product (pwg2) / (16*Gudhi::Persistence_representations::pi*sigma) << std::endl; std::cout << "Exact PSS kernel: " << pwgex1.compute_scalar_product (pwgex2) / (16*Gudhi::Persistence_representations::pi*sigma) << std::endl; diff --git a/src/Persistence_representations/example/sliced_wasserstein.cpp b/src/Persistence_representations/example/sliced_wasserstein.cpp index f1aeea5c..d37cb23c 100644 --- a/src/Persistence_representations/example/sliced_wasserstein.cpp +++ b/src/Persistence_representations/example/sliced_wasserstein.cpp @@ -30,8 +30,7 @@ using SW = Gudhi::Persistence_representations::Sliced_Wasserstein; int main(int argc, char** argv) { - std::vector > persistence1; - std::vector > persistence2; + Persistence_diagram persistence1, persistence2; persistence1.push_back(std::make_pair(1, 2)); persistence1.push_back(std::make_pair(6, 8)); diff --git a/src/Persistence_representations/include/gudhi/Landscape.h b/src/Persistence_representations/include/gudhi/Landscape.h index d6608a57..bbbca36b 100644 --- a/src/Persistence_representations/include/gudhi/Landscape.h +++ b/src/Persistence_representations/include/gudhi/Landscape.h @@ -40,64 +40,69 @@ #include #include -using PD = std::vector >; - namespace Gudhi { namespace Persistence_representations { /** * \class Landscape gudhi/Landscape.h - * \brief A class implementing the Landscapes. + * \brief A class implementing landscapes. * * \ingroup Persistence_representations * * \details * + * The landscape is a way to turn a persistence diagram into \f$L^2\f$ functions. Roughly, the idea is to see the boundaries of the rank functions as scalar functions taking values on the diagonal. + * See \cite bubenik_landscapes_2015 for more details. Here we provide a way to approximate such functions by computing their values on a set of samples. + * **/ class Landscape { - protected: - PD diagram; - int res_x, nb_ls; - double min_x, max_x; + protected: + Persistence_diagram diagram; + int res_x, nb_ls; + double min_x, max_x; public: - /** \brief Landscape constructor. - * \ingroup Landscape - * - */ - Landscape(PD _diagram, int _nb_ls = 5, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10){diagram = _diagram; nb_ls = _nb_ls; min_x = _min_x; max_x = _max_x; res_x = _res_x;} - - /** \brief Computes the landscape of a diagram. - * \ingroup Landscape - * - */ - std::vector > vectorize() { - std::vector > ls; for(int i = 0; i < nb_ls; i++) ls.emplace_back(); - int num_pts = diagram.size(); double step = (max_x - min_x)/res_x; - - for(int i = 0; i < res_x; i++){ - double x = min_x + i*step; double t = x / std::sqrt(2); std::vector events; - for(int j = 0; j < num_pts; j++){ - double px = diagram[j].first; double py = diagram[j].second; - if(t >= px && t <= py){ if(t >= (px+py)/2) events.push_back(std::sqrt(2)*(py-t)); else events.push_back(std::sqrt(2)*(t-px)); } - } - - std::sort(events.begin(), events.end(), [](const double & a, const double & b){return a > b;}); int nb_events = events.size(); - for (int j = 0; j < nb_ls; j++){ if(j < nb_events) ls[j].push_back(events[j]); else ls[j].push_back(0); } - } - - return ls; - } - - - - -}; - -} // namespace Landscape + /** \brief Landscape constructor. + * \ingroup Landscape + * + * @param[in] _diagram persistence diagram. + * @param[in] _nb_ls number of landscape functions. + * @param[in] _min_x minimum value of samples. + * @param[in] _max_x maximum value of samples. + * @param[in] _res_x number of samples. + * + */ + Landscape(const Persistence_diagram & _diagram, int _nb_ls = 5, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10){diagram = _diagram; nb_ls = _nb_ls; min_x = _min_x; max_x = _max_x; res_x = _res_x;} + + /** \brief Computes the landscape of a diagram. + * \ingroup Landscape + * + */ + std::vector > vectorize() const { + std::vector > ls; for(int i = 0; i < nb_ls; i++) ls.emplace_back(); + int num_pts = diagram.size(); double step = (max_x - min_x)/res_x; + + for(int i = 0; i < res_x; i++){ + double x = min_x + i*step; double t = x / std::sqrt(2); std::vector events; + for(int j = 0; j < num_pts; j++){ + double px = diagram[j].first; double py = diagram[j].second; + if(t >= px && t <= py){ if(t >= (px+py)/2) events.push_back(std::sqrt(2)*(py-t)); else events.push_back(std::sqrt(2)*(t-px)); } + } + + std::sort(events.begin(), events.end(), [](const double & a, const double & b){return a > b;}); int nb_events = events.size(); + for (int j = 0; j < nb_ls; j++){ if(j < nb_events) ls[j].push_back(events[j]); else ls[j].push_back(0); } + } + return ls; + } + + + + +}; // class Landscape +} // namespace Persistence_representations } // namespace Gudhi #endif // LANDSCAPE_H_ diff --git a/src/Persistence_representations/include/gudhi/Persistence_image.h b/src/Persistence_representations/include/gudhi/Persistence_image.h index 6c9f75b7..76b34d8d 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_image.h +++ b/src/Persistence_representations/include/gudhi/Persistence_image.h @@ -26,8 +26,8 @@ // gudhi include #include #include +#include #include -#include // standard include #include @@ -41,39 +41,49 @@ #include #include -using PD = std::vector >; -using Weight = std::function) >; - namespace Gudhi { namespace Persistence_representations { /** * \class Persistence_image gudhi/Persistence_image.h - * \brief A class implementing the Persistence Images. + * \brief A class implementing the persistence images. * * \ingroup Persistence_representations * * \details * + * Persistence images are a way to build images from persistence diagrams. Roughly, the idea is to center Gaussians on each diagram point, with a weight that usually depends on + * the distance to the diagonal, so that the diagram is turned into a function, and then to discretize the plane into pixels, and integrate this function on each pixel. + * See \cite Persistence_Images_2017 for more details. + * **/ class Persistence_image { protected: - PD diagram; - int res_x, res_y; - double min_x, max_x, min_y, max_y; - Weight weight; - double sigma; + Persistence_diagram diagram; + int res_x, res_y; + double min_x, max_x, min_y, max_y; + Weight weight; + double sigma; public: /** \brief Persistence Image constructor. * \ingroup Persistence_image * + * @param[in] _diagram persistence diagram. + * @param[in] _min_x minimum value of pixel abscissa. + * @param[in] _max_x maximum value of pixel abscissa. + * @param[in] _res_x number of pixels for the x-direction. + * @param[in] _min_y minimum value of pixel ordinate. + * @param[in] _max_y maximum value of pixel ordinate. + * @param[in] _res_y number of pixels for the y-direction. + * @param[in] _weight weight function for the Gaussians. + * @param[in] _sigma bandwidth parameter for the Gaussians. + * */ - Persistence_image(PD _diagram, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10, double _min_y = 0.0, double _max_y = 1.0, int _res_y = 10, - Weight _weight = Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight(1,1), double _sigma = 1.0){ + Persistence_image(const Persistence_diagram & _diagram, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10, double _min_y = 0.0, double _max_y = 1.0, int _res_y = 10, const Weight & _weight = arctan_weight(1,1), double _sigma = 1.0){ diagram = _diagram; min_x = _min_x; max_x = _max_x; res_x = _res_x; min_y = _min_y; max_y = _max_y; res_y = _res_y, weight = _weight; sigma = _sigma; } @@ -81,7 +91,7 @@ class Persistence_image { * \ingroup Persistence_image * */ - std::vector > vectorize() { + std::vector > vectorize() const { std::vector > im; for(int i = 0; i < res_y; i++) im.emplace_back(); double step_x = (max_x - min_x)/res_x; double step_y = (max_y - min_y)/res_y; @@ -109,9 +119,8 @@ class Persistence_image { -}; - -} // namespace Persistence_image +}; // class Persistence_image +} // namespace Persistence_representations } // namespace Gudhi #endif // PERSISTENCE_IMAGE_H_ diff --git a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h index 9a63fccd..76c43e65 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h +++ b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h @@ -26,6 +26,7 @@ // gudhi include #include #include +#include // standard include #include @@ -39,19 +40,16 @@ #include #include -using PD = std::vector >; -using Weight = std::function) >; - namespace Gudhi { namespace Persistence_representations { /** * \class Persistence_weighted_gaussian gudhi/Persistence_weighted_gaussian.h - * \brief A class implementing the Persistence Weighted Gaussian Kernel and a specific case of it called the Persistence Scale Space Kernel. + * \brief A class implementing the Persistence Weighted Gaussian kernel and a specific case thereof called the Persistence Scale Space kernel. * * \ingroup Persistence_representations * * \details - * The Persistence Weighted Gaussian Kernel is built with Gaussian Kernel Mean Embedding, meaning that each persistence diagram is first + * The Persistence Weighted Gaussian kernel is built with Gaussian Kernel Mean Embedding, meaning that each persistence diagram is first * sent to the Hilbert space of a Gaussian kernel with bandwidth parameter \f$\sigma >0\f$ using a weighted mean embedding \f$\Phi\f$: * * \f$ \Phi\,:\,D\,\rightarrow\,\sum_{p\in D}\,w(p)\,{\rm exp}\left(-\frac{\|p-\cdot\|_2^2}{2\sigma^2}\right) \f$, @@ -69,59 +67,41 @@ namespace Persistence_representations { * in the diagrams. This time can be improved by computing approximations of the kernel * with \f$m\f$ Fourier features \cite Rahimi07randomfeatures. In that case, the computation time becomes \f$O(mn)\f$. * - * The Persistence Scale Space Kernel is a Persistence Weighted Gaussian Kernel between modified diagrams: + * The Persistence Scale Space kernel is a Persistence Weighted Gaussian kernel between modified diagrams: * the symmetric of each point with respect to the diagonal is first added in each diagram, and then the weight function * is set to be +1 if the point is above the diagonal and -1 otherwise. * - * For more details, please consult Persistence Weighted Kernel for Topological Data Analysis\cite Kusano_Fukumizu_Hiraoka_PWGK - * and A Stable Multi-Scale Kernel for Topological Machine Learning\cite Reininghaus_Huber_ALL_PSSK . - * It implements the following concepts: Topological_data_with_distances, Topological_data_with_scalar_product. + * For more details, please see \cite Kusano_Fukumizu_Hiraoka_PWGK + * and \cite Reininghaus_Huber_ALL_PSSK . * **/ class Persistence_weighted_gaussian{ protected: - PD diagram; + Persistence_diagram diagram; Weight weight; double sigma; int approx; public: - /** \brief Persistence Weighted Gaussian Kernel constructor. + /** \brief Persistence Weighted Gaussian kernel constructor. * \ingroup Persistence_weighted_gaussian * * @param[in] _diagram persistence diagram. - * @param[in] _sigma bandwidth parameter of the Gaussian Kernel used for the Kernel Mean Embedding of the diagrams. + * @param[in] _sigma bandwidth parameter of the Gaussian kernel used for the Kernel Mean Embedding of the diagrams. * @param[in] _approx number of random Fourier features in case of approximate computation, set to -1 for exact computation. * @param[in] _weight weight function for the points in the diagrams. * */ - Persistence_weighted_gaussian(PD _diagram, double _sigma = 1.0, int _approx = 1000, Weight _weight = arctan_weight(1,1)){diagram = _diagram; sigma = _sigma; approx = _approx; weight = _weight;} - - PD get_diagram() const {return this->diagram;} - double get_sigma() const {return this->sigma;} - int get_approx() const {return this->approx;} - Weight get_weight() const {return this->weight;} + Persistence_weighted_gaussian(const Persistence_diagram & _diagram, double _sigma = 1.0, int _approx = 1000, const Weight & _weight = arctan_weight(1,1)){diagram = _diagram; sigma = _sigma; approx = _approx; weight = _weight;} // ********************************** // Utils. // ********************************** - /** \brief Specific weight of Persistence Scale Space Kernel. - * \ingroup Persistence_weighted_gaussian - * - * @param[in] p point in 2D. - * - */ - static double pss_weight(std::pair p) {if(p.second > p.first) return 1; else return -1;} - static double linear_weight(std::pair p) {return std::abs(p.second - p.first);} - static double const_weight(std::pair p) {return 1;} - static std::function) > arctan_weight(double C, double power) {return [=](std::pair p){return C * atan(std::pow(std::abs(p.second - p.first), power));};} - - - std::vector > Fourier_feat(PD diag, std::vector > z, Weight weight = arctan_weight(1,1)){ + std::vector > Fourier_feat(const Persistence_diagram & diag, const std::vector > & z, const Weight & weight = arctan_weight(1,1)) const { int md = diag.size(); std::vector > b; int mz = z.size(); for(int i = 0; i < mz; i++){ double d1 = 0; double d2 = 0; double zx = z[i].first; double zy = z[i].second; @@ -135,7 +115,7 @@ class Persistence_weighted_gaussian{ return b; } - std::vector > random_Fourier(double sigma, int m = 1000){ + std::vector > random_Fourier(double sigma, int m = 1000) const { std::normal_distribution distrib(0,1); std::vector > z; std::random_device rd; for(int i = 0; i < m; i++){ std::mt19937 e1(rd()); std::mt19937 e2(rd()); @@ -154,12 +134,14 @@ class Persistence_weighted_gaussian{ /** \brief Evaluation of the kernel on a pair of diagrams. * \ingroup Persistence_weighted_gaussian * - * @param[in] second other instance of class Persistence_weighted_gaussian. Warning: sigma, approx and weight parameters need to be the same for both instances!!! + * @pre sigma, approx and weight attributes need to be the same for both instances. + * @param[in] second other instance of class Persistence_weighted_gaussian. * */ - double compute_scalar_product(Persistence_weighted_gaussian second){ + double compute_scalar_product(const Persistence_weighted_gaussian & second) const { - PD diagram1 = this->diagram; PD diagram2 = second.diagram; + GUDHI_CHECK(this->sigma != second.sigma || this->approx != second.approx || this->weight != second.weight, std::invalid_argument("Error: different values for representations")); + Persistence_diagram diagram1 = this->diagram; Persistence_diagram diagram2 = second.diagram; if(this->approx == -1){ int num_pts1 = diagram1.size(); int num_pts2 = diagram2.size(); double k = 0; @@ -171,7 +153,7 @@ class Persistence_weighted_gaussian{ return k; } else{ - std::vector > z = random_Fourier(this->sigma, this->approx); + std::vector > z = random_Fourier(this->sigma, this->approx); std::vector > b1 = Fourier_feat(diagram1,z,this->weight); std::vector > b2 = Fourier_feat(diagram2,z,this->weight); double d = 0; for(int i = 0; i < this->approx; i++) d += b1[i].first*b2[i].first + b1[i].second*b2[i].second; @@ -182,20 +164,18 @@ class Persistence_weighted_gaussian{ /** \brief Evaluation of the distance between images of diagrams in the Hilbert space of the kernel. * \ingroup Persistence_weighted_gaussian * - * @param[in] second other instance of class Persistence_weighted_gaussian. Warning: sigma, approx and weight parameters need to be the same for both instances!!! + * @pre sigma, approx and weight attributes need to be the same for both instances. + * @param[in] second other instance of class Persistence_weighted_gaussian. * */ - double distance(Persistence_weighted_gaussian second) { - if(this->sigma != second.get_sigma() || this->approx != second.get_approx()){ - std::cout << "Error: different representations!" << std::endl; return 0; - } - else return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), 0.5); + double distance(const Persistence_weighted_gaussian & second) const { + GUDHI_CHECK(this->sigma != second.sigma || this->approx != second.approx || this->weight != second.weight, std::invalid_argument("Error: different values for representations")); + return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), 0.5); } -}; - -} // namespace Persistence_weighted_gaussian +}; // class Persistence_weighted_gaussian +} // namespace Persistence_representations } // namespace Gudhi #endif // PERSISTENCE_WEIGHTED_GAUSSIAN_H_ diff --git a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h index 6a9a607e..235918fe 100644 --- a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h +++ b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h @@ -40,19 +40,17 @@ #include #include -using PD = std::vector >; - namespace Gudhi { namespace Persistence_representations { /** * \class Sliced_Wasserstein gudhi/Sliced_Wasserstein.h - * \brief A class implementing the Sliced Wasserstein Kernel. + * \brief A class implementing the Sliced Wasserstein kernel. * * \ingroup Persistence_representations * * \details - * The Sliced Wasserstein Kernel is defined as a Gaussian-like Kernel between persistence diagrams, where the distance used for + * The Sliced Wasserstein kernel is defined as a Gaussian-like kernel between persistence diagrams, where the distance used for * comparison is the Sliced Wasserstein distance \f$SW\f$ between persistence diagrams, defined as the integral of the 1-norm * between the sorted projections of the diagrams onto all lines passing through the origin: * @@ -65,15 +63,14 @@ namespace Persistence_representations { * * \f$ k(D_1,D_2) = {\rm exp}\left(-\frac{SW(D_1,D_2)}{2\sigma^2}\right).\f$ * - * For more details, please consult Sliced Wasserstein Kernel for Persistence Diagrams\cite pmlr-v70-carriere17a . - * It implements the following concepts: Topological_data_with_distances, Topological_data_with_scalar_product. + * For more details, please see \cite pmlr-v70-carriere17a . * **/ class Sliced_Wasserstein { protected: - PD diagram; + Persistence_diagram diagram; int approx; double sigma; std::vector > projections, projections_diagonal; @@ -107,7 +104,7 @@ class Sliced_Wasserstein { } - /** \brief Sliced Wasserstein Kernel constructor. + /** \brief Sliced Wasserstein kernel constructor. * \ingroup Sliced_Wasserstein * * @param[in] _diagram persistence diagram. @@ -115,21 +112,14 @@ class Sliced_Wasserstein { * @param[in] _approx number of directions used to approximate the integral in the Sliced Wasserstein distance, set to -1 for exact computation. * */ - Sliced_Wasserstein(PD _diagram, double _sigma = 1.0, int _approx = 100){diagram = _diagram; approx = _approx; sigma = _sigma; build_rep();} - - PD get_diagram() const {return this->diagram;} - int get_approx() const {return this->approx;} - double get_sigma() const {return this->sigma;} - - - + Sliced_Wasserstein(const Persistence_diagram & _diagram, double _sigma = 1.0, int _approx = 100){diagram = _diagram; approx = _approx; sigma = _sigma; build_rep();} // ********************************** // Utils. // ********************************** // Compute the angle formed by two points of a PD - double compute_angle(PD diag, int i, int j){ + double compute_angle(const Persistence_diagram & diag, int i, int j) const { std::pair vect; double x1,y1, x2,y2; x1 = diag[i].first; y1 = diag[i].second; x2 = diag[j].first; y2 = diag[j].second; @@ -150,7 +140,7 @@ class Sliced_Wasserstein { } // Compute the integral of |cos()| between alpha and beta, valid only if alpha is in [-pi,pi] and beta-alpha is in [0,pi] - double compute_int_cos(const double & alpha, const double & beta){ + double compute_int_cos(double alpha, double beta) const { double res = 0; if (alpha >= 0 && alpha <= pi){ if (cos(alpha) >= 0){ @@ -175,13 +165,13 @@ class Sliced_Wasserstein { return res; } - double compute_int(const double & theta1, const double & theta2, const int & p, const int & q, const PD & PD1, const PD & PD2){ - double norm = std::sqrt( (PD1[p].first-PD2[q].first)*(PD1[p].first-PD2[q].first) + (PD1[p].second-PD2[q].second)*(PD1[p].second-PD2[q].second) ); + double compute_int(double theta1, double theta2, int p, int q, const Persistence_diagram & diag1, const Persistence_diagram & diag2) const { + double norm = std::sqrt( (diag1[p].first-diag2[q].first)*(diag1[p].first-diag2[q].first) + (diag1[p].second-diag2[q].second)*(diag1[p].second-diag2[q].second) ); double angle1; - if (PD1[p].first > PD2[q].first) - angle1 = theta1 - asin( (PD1[p].second-PD2[q].second)/norm ); + if (diag1[p].first > diag2[q].first) + angle1 = theta1 - asin( (diag1[p].second-diag2[q].second)/norm ); else - angle1 = theta1 - asin( (PD2[q].second-PD1[p].second)/norm ); + angle1 = theta1 - asin( (diag2[q].second-diag1[p].second)/norm ); double angle2 = angle1 + theta2 - theta1; double integral = compute_int_cos(angle1,angle2); return norm*integral; @@ -197,134 +187,133 @@ class Sliced_Wasserstein { /** \brief Evaluation of the Sliced Wasserstein Distance between a pair of diagrams. * \ingroup Sliced_Wasserstein * + * @pre approx attribute needs to be the same for both instances. * @param[in] second other instance of class Sliced_Wasserstein. - * For warning in red: - * @warning approx parameter needs to be the same for both instances. + * * */ - double compute_sliced_wasserstein_distance(Sliced_Wasserstein second) { + double compute_sliced_wasserstein_distance(const Sliced_Wasserstein & second) const { - GUDHI_CHECK(this->approx != second.approx, std::invalid_argument("Error: different approx values for representations")); + GUDHI_CHECK(this->approx != second.approx, std::invalid_argument("Error: different approx values for representations")); - PD diagram1 = this->diagram; PD diagram2 = second.diagram; double sw = 0; + Persistence_diagram diagram1 = this->diagram; Persistence_diagram diagram2 = second.diagram; double sw = 0; - if(this->approx == -1){ + if(this->approx == -1){ - // Add projections onto diagonal. - int n1, n2; n1 = diagram1.size(); n2 = diagram2.size(); double max_ordinate = std::numeric_limits::lowest(); - for (int i = 0; i < n2; i++){ - max_ordinate = std::max(max_ordinate, diagram2[i].second); - diagram1.emplace_back( (diagram2[i].first+diagram2[i].second)/2, (diagram2[i].first+diagram2[i].second)/2 ); - } - for (int i = 0; i < n1; i++){ - max_ordinate = std::max(max_ordinate, diagram1[i].second); - diagram2.emplace_back( (diagram1[i].first+diagram1[i].second)/2, (diagram1[i].first+diagram1[i].second)/2 ); - } - int num_pts_dgm = diagram1.size(); - - // Slightly perturb the points so that the PDs are in generic positions. - int mag = 0; while(max_ordinate > 10){mag++; max_ordinate/=10;} - double thresh = pow(10,-5+mag); - srand(time(NULL)); - for (int i = 0; i < num_pts_dgm; i++){ - diagram1[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); diagram1[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); - diagram2[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); diagram2[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); - } + // Add projections onto diagonal. + int n1, n2; n1 = diagram1.size(); n2 = diagram2.size(); double max_ordinate = std::numeric_limits::lowest(); + for (int i = 0; i < n2; i++){ + max_ordinate = std::max(max_ordinate, diagram2[i].second); + diagram1.emplace_back( (diagram2[i].first+diagram2[i].second)/2, (diagram2[i].first+diagram2[i].second)/2 ); + } + for (int i = 0; i < n1; i++){ + max_ordinate = std::max(max_ordinate, diagram1[i].second); + diagram2.emplace_back( (diagram1[i].first+diagram1[i].second)/2, (diagram1[i].first+diagram1[i].second)/2 ); + } + int num_pts_dgm = diagram1.size(); + + // Slightly perturb the points so that the PDs are in generic positions. + int mag = 0; while(max_ordinate > 10){mag++; max_ordinate/=10;} + double thresh = pow(10,-5+mag); + srand(time(NULL)); + for (int i = 0; i < num_pts_dgm; i++){ + diagram1[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); diagram1[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); + diagram2[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); diagram2[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); + } - // Compute all angles in both PDs. - std::vector > > angles1, angles2; - for (int i = 0; i < num_pts_dgm; i++){ - for (int j = i+1; j < num_pts_dgm; j++){ - double theta1 = compute_angle(diagram1,i,j); double theta2 = compute_angle(diagram2,i,j); - angles1.emplace_back(theta1, std::pair(i,j)); - angles2.emplace_back(theta2, std::pair(i,j)); - } + // Compute all angles in both PDs. + std::vector > > angles1, angles2; + for (int i = 0; i < num_pts_dgm; i++){ + for (int j = i+1; j < num_pts_dgm; j++){ + double theta1 = compute_angle(diagram1,i,j); double theta2 = compute_angle(diagram2,i,j); + angles1.emplace_back(theta1, std::pair(i,j)); + angles2.emplace_back(theta2, std::pair(i,j)); } + } - // Sort angles. - std::sort(angles1.begin(), angles1.end(), [=](std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);}); - std::sort(angles2.begin(), angles2.end(), [=](std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);}); - - // Initialize orders of the points of both PDs (given by ordinates when theta = -pi/2). - std::vector orderp1, orderp2; - for (int i = 0; i < num_pts_dgm; i++){ orderp1.push_back(i); orderp2.push_back(i); } - std::sort( orderp1.begin(), orderp1.end(), [=](int i, int j){ if(diagram1[i].second != diagram1[j].second) return (diagram1[i].second < diagram1[j].second); else return (diagram1[i].first > diagram1[j].first); } ); - std::sort( orderp2.begin(), orderp2.end(), [=](int i, int j){ if(diagram2[i].second != diagram2[j].second) return (diagram2[i].second < diagram2[j].second); else return (diagram2[i].first > diagram2[j].first); } ); - - // Find the inverses of the orders. - std::vector order1(num_pts_dgm); std::vector order2(num_pts_dgm); - for(int i = 0; i < num_pts_dgm; i++) for (int j = 0; j < num_pts_dgm; j++) if(orderp1[j] == i){ order1[i] = j; break; } - for(int i = 0; i < num_pts_dgm; i++) for (int j = 0; j < num_pts_dgm; j++) if(orderp2[j] == i){ order2[i] = j; break; } - - // Record all inversions of points in the orders as theta varies along the positive half-disk. - std::vector > > anglePerm1(num_pts_dgm); - std::vector > > anglePerm2(num_pts_dgm); - - int m1 = angles1.size(); - for (int i = 0; i < m1; i++){ - double theta = angles1[i].first; int p = angles1[i].second.first; int q = angles1[i].second.second; - anglePerm1[order1[p]].emplace_back(p,theta); - anglePerm1[order1[q]].emplace_back(q,theta); - int a = order1[p]; int b = order1[q]; order1[p] = b; order1[q] = a; - } + // Sort angles. + std::sort(angles1.begin(), angles1.end(), [=](std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);}); + std::sort(angles2.begin(), angles2.end(), [=](std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);}); + + // Initialize orders of the points of both PDs (given by ordinates when theta = -pi/2). + std::vector orderp1, orderp2; + for (int i = 0; i < num_pts_dgm; i++){ orderp1.push_back(i); orderp2.push_back(i); } + std::sort( orderp1.begin(), orderp1.end(), [=](int i, int j){ if(diagram1[i].second != diagram1[j].second) return (diagram1[i].second < diagram1[j].second); else return (diagram1[i].first > diagram1[j].first); } ); + std::sort( orderp2.begin(), orderp2.end(), [=](int i, int j){ if(diagram2[i].second != diagram2[j].second) return (diagram2[i].second < diagram2[j].second); else return (diagram2[i].first > diagram2[j].first); } ); + + // Find the inverses of the orders. + std::vector order1(num_pts_dgm); std::vector order2(num_pts_dgm); + for(int i = 0; i < num_pts_dgm; i++) for (int j = 0; j < num_pts_dgm; j++) if(orderp1[j] == i){ order1[i] = j; break; } + for(int i = 0; i < num_pts_dgm; i++) for (int j = 0; j < num_pts_dgm; j++) if(orderp2[j] == i){ order2[i] = j; break; } + + // Record all inversions of points in the orders as theta varies along the positive half-disk. + std::vector > > anglePerm1(num_pts_dgm); + std::vector > > anglePerm2(num_pts_dgm); + + int m1 = angles1.size(); + for (int i = 0; i < m1; i++){ + double theta = angles1[i].first; int p = angles1[i].second.first; int q = angles1[i].second.second; + anglePerm1[order1[p]].emplace_back(p,theta); + anglePerm1[order1[q]].emplace_back(q,theta); + int a = order1[p]; int b = order1[q]; order1[p] = b; order1[q] = a; + } - int m2 = angles2.size(); - for (int i = 0; i < m2; i++){ - double theta = angles2[i].first; int p = angles2[i].second.first; int q = angles2[i].second.second; - anglePerm2[order2[p]].emplace_back(p,theta); - anglePerm2[order2[q]].emplace_back(q,theta); - int a = order2[p]; int b = order2[q]; order2[p] = b; order2[q] = a; - } + int m2 = angles2.size(); + for (int i = 0; i < m2; i++){ + double theta = angles2[i].first; int p = angles2[i].second.first; int q = angles2[i].second.second; + anglePerm2[order2[p]].emplace_back(p,theta); + anglePerm2[order2[q]].emplace_back(q,theta); + int a = order2[p]; int b = order2[q]; order2[p] = b; order2[q] = a; + } - for (int i = 0; i < num_pts_dgm; i++){ - anglePerm1[order1[i]].emplace_back(i,pi/2); - anglePerm2[order2[i]].emplace_back(i,pi/2); - } + for (int i = 0; i < num_pts_dgm; i++){ + anglePerm1[order1[i]].emplace_back(i,pi/2); + anglePerm2[order2[i]].emplace_back(i,pi/2); + } - // Compute the SW distance with the list of inversions. - for (int i = 0; i < num_pts_dgm; i++){ - std::vector > u,v; u = anglePerm1[i]; v = anglePerm2[i]; - double theta1, theta2; theta1 = -pi/2; - unsigned int ku, kv; ku = 0; kv = 0; theta2 = std::min(u[ku].second,v[kv].second); - while(theta1 != pi/2){ - if(diagram1[u[ku].first].first != diagram2[v[kv].first].first || diagram1[u[ku].first].second != diagram2[v[kv].first].second) - if(theta1 != theta2) - sw += compute_int(theta1, theta2, u[ku].first, v[kv].first, diagram1, diagram2); - theta1 = theta2; - if ( (theta2 == u[ku].second) && ku < u.size()-1 ) ku++; - if ( (theta2 == v[kv].second) && kv < v.size()-1 ) kv++; - theta2 = std::min(u[ku].second, v[kv].second); - } + // Compute the SW distance with the list of inversions. + for (int i = 0; i < num_pts_dgm; i++){ + std::vector > u,v; u = anglePerm1[i]; v = anglePerm2[i]; + double theta1, theta2; theta1 = -pi/2; + unsigned int ku, kv; ku = 0; kv = 0; theta2 = std::min(u[ku].second,v[kv].second); + while(theta1 != pi/2){ + if(diagram1[u[ku].first].first != diagram2[v[kv].first].first || diagram1[u[ku].first].second != diagram2[v[kv].first].second) + if(theta1 != theta2) + sw += compute_int(theta1, theta2, u[ku].first, v[kv].first, diagram1, diagram2); + theta1 = theta2; + if ( (theta2 == u[ku].second) && ku < u.size()-1 ) ku++; + if ( (theta2 == v[kv].second) && kv < v.size()-1 ) kv++; + theta2 = std::min(u[ku].second, v[kv].second); } } + } - else{ - - double step = pi/this->approx; - - for (int i = 0; i < this->approx; i++){ + else{ - std::vector v1; std::vector l1 = this->projections[i]; std::vector l1bis = second.projections_diagonal[i]; std::merge(l1.begin(), l1.end(), l1bis.begin(), l1bis.end(), std::back_inserter(v1)); - std::vector v2; std::vector l2 = second.projections[i]; std::vector l2bis = this->projections_diagonal[i]; std::merge(l2.begin(), l2.end(), l2bis.begin(), l2bis.end(), std::back_inserter(v2)); - int n = v1.size(); double f = 0; - for (int j = 0; j < n; j++) f += std::abs(v1[j] - v2[j]); - sw += f*step; + double step = pi/this->approx; + for (int i = 0; i < this->approx; i++){ - } + std::vector v1; std::vector l1 = this->projections[i]; std::vector l1bis = second.projections_diagonal[i]; std::merge(l1.begin(), l1.end(), l1bis.begin(), l1bis.end(), std::back_inserter(v1)); + std::vector v2; std::vector l2 = second.projections[i]; std::vector l2bis = this->projections_diagonal[i]; std::merge(l2.begin(), l2.end(), l2bis.begin(), l2bis.end(), std::back_inserter(v2)); + int n = v1.size(); double f = 0; + for (int j = 0; j < n; j++) f += std::abs(v1[j] - v2[j]); + sw += f*step; } + } - return sw/pi; + return sw/pi; } /** \brief Evaluation of the kernel on a pair of diagrams. * \ingroup Sliced_Wasserstein * - * @param[in] second other instance of class Sliced_Wasserstein. Warning: sigma and approx parameters need to be the same for both instances!!! + * @pre approx and sigma attributes need to be the same for both instances. + * @param[in] second other instance of class Sliced_Wasserstein. * */ - double compute_scalar_product(Sliced_Wasserstein second){ + double compute_scalar_product(const Sliced_Wasserstein & second) const { GUDHI_CHECK(this->sigma != second.sigma, std::invalid_argument("Error: different sigma values for representations")); return std::exp(-compute_sliced_wasserstein_distance(second)/(2*this->sigma*this->sigma)); } @@ -332,10 +321,11 @@ class Sliced_Wasserstein { /** \brief Evaluation of the distance between images of diagrams in the Hilbert space of the kernel. * \ingroup Sliced_Wasserstein * - * @param[in] second other instance of class Sliced_Wasserstein. Warning: sigma and approx parameters need to be the same for both instances!!! + * @pre approx and sigma attributes need to be the same for both instances. + * @param[in] second other instance of class Sliced_Wasserstein. * */ - double distance(Sliced_Wasserstein second) { + double distance(const Sliced_Wasserstein & second) const { GUDHI_CHECK(this->sigma != second.sigma, std::invalid_argument("Error: different sigma values for representations")); return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), 0.5); } @@ -343,9 +333,8 @@ class Sliced_Wasserstein { -}; - -} // namespace Sliced_Wasserstein +}; // class Sliced_Wasserstein +} // namespace Persistence_representations } // namespace Gudhi #endif // SLICED_WASSERSTEIN_H_ diff --git a/src/Persistence_representations/include/gudhi/common_persistence_representations.h b/src/Persistence_representations/include/gudhi/common_persistence_representations.h index 90f2626d..884fce58 100644 --- a/src/Persistence_representations/include/gudhi/common_persistence_representations.h +++ b/src/Persistence_representations/include/gudhi/common_persistence_representations.h @@ -28,17 +28,28 @@ #include #include +/** + * In this module, we use the name Persistence_diagram for the representation of a diagram in a vector of pairs of two double. + */ +using Persistence_diagram = std::vector >; + +/** + * In this module, we use the name Weight for the representation of a function taking a pair of two double and returning a double. + */ +using Weight = std::function) >; + namespace Gudhi { namespace Persistence_representations { // this file contain an implementation of some common procedures used in Persistence_representations. static constexpr double pi = boost::math::constants::pi(); + // double epsi = std::numeric_limits::epsilon(); double epsi = 0.000005; /** * A procedure used to compare doubles. Typically given two doubles A and B, comparing A == B is not good idea. In this - *case, we use the procedure almostEqual with the epsi defined at + * case, we use the procedure almostEqual with the epsi defined at * the top of the file. Setting up the epsi gives the user a tolerance on what should be consider equal. **/ inline bool almost_equal(double a, double b) { @@ -55,8 +66,7 @@ double birth_plus_deaths(std::pair a) { return a.first + a.secon // landscapes /** - * Given two points in R^2, the procedure compute the parameters A and B of the line y = Ax + B that crosses those two - *points. + * Given two points in R^2, the procedure compute the parameters A and B of the line y = Ax + B that crosses those two points. **/ std::pair compute_parameters_of_a_line(std::pair p1, std::pair p2) { double a = (p2.second - p1.second) / (p2.first - p1.first); @@ -66,8 +76,7 @@ std::pair compute_parameters_of_a_line(std::pair // landscapes /** - * This procedure given two points which lies on the opposite sides of x axis, compute x for which the line connecting - *those two points crosses x axis. + * This procedure given two points which lies on the opposite sides of x axis, compute x for which the line connecting those two points crosses x axis. **/ double find_zero_of_a_line_segment_between_those_two_points(std::pair p1, std::pair p2) { @@ -91,8 +100,7 @@ double find_zero_of_a_line_segment_between_those_two_points(std::pair f, std::pair s) { if (f.first < s.first) { -- cgit v1.2.3 From 380571047eac55b826fe8e0654f9ed9b64f22ffa Mon Sep 17 00:00:00 2001 From: mcarrier Date: Mon, 23 Apr 2018 15:22:37 +0000 Subject: added weight_functions.h git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3388 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 71f741e7ca2fcacc877d44e4d6b633f283526f2f --- .../include/gudhi/Weight_functions.h | 81 ++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/Persistence_representations/include/gudhi/Weight_functions.h diff --git a/src/Persistence_representations/include/gudhi/Weight_functions.h b/src/Persistence_representations/include/gudhi/Weight_functions.h new file mode 100644 index 00000000..78de406d --- /dev/null +++ b/src/Persistence_representations/include/gudhi/Weight_functions.h @@ -0,0 +1,81 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 WEIGHT_FUNCTIONS_H_ +#define WEIGHT_FUNCTIONS_H_ + +// gudhi include +#include +#include + +// standard include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Gudhi { +namespace Persistence_representations { + +/** \fn static double pss_weight(std::pair p) + * \brief Persistence Scale Space kernel weight function. + * \ingroup Persistence_representations + * + * @param[in] p point in 2D. + */ +static double pss_weight(std::pair p) {if(p.second > p.first) return 1; else return -1;} + +/** \fn static double linear_weight(std::pair p) + * \brief Linear weight function. + * \ingroup Persistence_representations + * + * @param[in] p point in 2D. + */ +static double linear_weight(std::pair p) {return std::abs(p.second - p.first);} + +/** \fn static double const_weight(std::pair p) + * \brief Constant weight function. + * \ingroup Persistence_representations + * + * @param[in] p point in 2D. + */ +static double const_weight(std::pair p) {return 1;} + +/** \fn static std::function) > arctan_weight(double C, double alpha) + * \brief Returns the arctan weight function with parameters C and alpha. + * \ingroup Persistence_representations + * + * @param[in] C positive constant. + * @param[in] alpha positive power. + */ +static std::function) > arctan_weight(double C, double alpha) {return [=](std::pair p){return C * atan(std::pow(std::abs(p.second - p.first), alpha));};} + +} // namespace Persistence_representations +} // namespace Gudhi + +#endif // WEIGHT_FUNCTIONS_H_ -- cgit v1.2.3 From ea11100d803c86a0fd5cfec1b9431110b48d87c9 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Mon, 23 Apr 2018 15:44:55 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3389 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 1569e7e9d47af54fe85e736583e53c99f1178439 --- src/cython/include/Kernels_interface.h | 12 ++++++------ src/cython/include/Vectors_interface.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cython/include/Kernels_interface.h b/src/cython/include/Kernels_interface.h index 0da28245..03050408 100644 --- a/src/cython/include/Kernels_interface.h +++ b/src/cython/include/Kernels_interface.h @@ -46,8 +46,8 @@ namespace persistence_diagram { } double pwg(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N, double C, double p) { - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, sigma, N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight(C,p)); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, sigma, N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight(C,p)); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, sigma, N, Gudhi::Persistence_representations::arctan_weight(C,p)); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, sigma, N, Gudhi::Persistence_representations::arctan_weight(C,p)); return pwg1.compute_scalar_product(pwg2); } @@ -55,15 +55,15 @@ namespace persistence_diagram { std::vector> pd1 = diag1; int numpts = diag1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(diag1[i].second,diag1[i].first); std::vector> pd2 = diag2; numpts = diag2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(diag2[i].second,diag2[i].first); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(pd1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(pd2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(pd1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(pd2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); return pwg1.compute_scalar_product (pwg2) / (16*Gudhi::Persistence_representations::pi*sigma); } double pss_sym(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::Persistence_weighted_gaussian::pss_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); return pwg1.compute_scalar_product (pwg2) / (16*Gudhi::Persistence_representations::pi*sigma); } diff --git a/src/cython/include/Vectors_interface.h b/src/cython/include/Vectors_interface.h index 49d28e7c..7e191f2a 100644 --- a/src/cython/include/Vectors_interface.h +++ b/src/cython/include/Vectors_interface.h @@ -44,9 +44,9 @@ namespace persistence_diagram { std::vector > compute_pim(const std::vector >& diag, double min_x, double max_x, int res_x, double min_y, double max_y, int res_y, std::string weight, double sigma, double C, double p) { Weight weight_fn; - if(weight.compare("linear") == 0) weight_fn = Gudhi::Persistence_representations::Persistence_weighted_gaussian::linear_weight; - if(weight.compare("arctan") == 0) weight_fn = Gudhi::Persistence_representations::Persistence_weighted_gaussian::arctan_weight(C,p); - if(weight.compare("const") == 0) weight_fn = Gudhi::Persistence_representations::Persistence_weighted_gaussian::const_weight; + if(weight.compare("linear") == 0) weight_fn = Gudhi::Persistence_representations::linear_weight; + if(weight.compare("arctan") == 0) weight_fn = Gudhi::Persistence_representations::arctan_weight(C,p); + if(weight.compare("const") == 0) weight_fn = Gudhi::Persistence_representations::const_weight; Gudhi::Persistence_representations::Persistence_image P(diag, min_x, max_x, res_x, min_y, max_y, res_y, weight_fn, sigma); return P.vectorize(); } -- cgit v1.2.3 From df5a7d97392469239e1f00ea50da5eb2b378b7e7 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Wed, 25 Apr 2018 16:47:55 +0000 Subject: renamed landscape and image code to make them fit in Pawel's denomination git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3396 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 5269a65810a1164cb61bcb34d23046e6a48ec355 --- .../doc/Persistence_representations_doc.h | 85 ++++++++++++---------- .../example/CMakeLists.txt | 18 ++--- src/cython/include/Kernels_interface.h | 1 + src/cython/include/Vectors_interface.h | 10 +-- 4 files changed, 61 insertions(+), 53 deletions(-) diff --git a/src/Persistence_representations/doc/Persistence_representations_doc.h b/src/Persistence_representations/doc/Persistence_representations_doc.h index ca283017..a7691324 100644 --- a/src/Persistence_representations/doc/Persistence_representations_doc.h +++ b/src/Persistence_representations/doc/Persistence_representations_doc.h @@ -128,33 +128,35 @@ namespace Persistence_representations { function \f$L : \mathbb{N} \times \mathbb{R} \to [0,\infty)\f$ of two variables, if we define \f$L(k,t) = \lambda_k(t)\f$. - The detailed description of algorithms used to compute persistence landscapes can be found in - \cite bubenik_dlotko_landscapes_2016. - Note that this implementation provides exact representation of landscapes. That have many advantages, but also a few - drawbacks. For instance, as discussed - in \cite bubenik_dlotko_landscapes_2016, the exact representation of landscape may be of quadratic size with respect - to the input persistence diagram. It may therefore happen - that, for very large diagrams, using this representation may be memory--prohibitive. In such a case, there are two - possible ways to proceed: - - \li Use non exact representation on a grid described in the Section \ref sec_landscapes_on_grid. + The detailed description of algorithms used to compute persistence landscapes can be found in \cite bubenik_dlotko_landscapes_2016. + Note that this implementation provides exact representation of landscapes. That have many advantages, but also a few drawbacks. + For instance, as discussed in \cite bubenik_dlotko_landscapes_2016, the exact representation of landscape may be of quadratic size with respect + to the input persistence diagram. It may therefore happen that, for very large diagrams, using this representation may be memory--prohibitive. + In such a case, there are two possible ways to proceed: + + \li Use representation on a grid---see section \ref sec_landscapes_on_grid. \li Compute just a number of initial nonzero landscapes. This option is available from C++ level as a last parameter of the constructor of persistence landscape (set by default to std::numeric_limits::max()). \section sec_landscapes_on_grid Persistence Landscapes on a grid + Reference manual: \ref Gudhi::Persistence_representations::Persistence_landscape_on_grid
- This is an alternative, not--exact, representation of persistence landscapes defined in the Section \ref - sec_persistence_landscapes. Unlike in the Section \ref sec_persistence_landscapes we build a - representation of persistence landscape by sampling its values on a finite, equally distributed grid of points. - Since, the persistence landscapes that originate from persistence diagrams have slope \f$1\f$ or \f$-1\f$, we have an - estimate of a region between the grid points where the landscape cab be located. - That allows to estimate an error make when performing various operations on landscape. Note that for average - landscapes the slope is in range \f$[-1,1]\f$ and similar estimate can be used. + Reference manual: \ref Gudhi::Persistence_representations::Persistence_landscape_on_grid_exact
+ + Here, we provide alternative, not exact, representations of persistence landscapes defined in Section \ref sec_persistence_landscapes. + Unlike Section \ref sec_persistence_landscapes, we build representations of persistence landscapes by evaluating the landscape functions on a finite, equally distributed grid of points. + We propose two different representations depending on whether the persistence intervals are also mapped on the grid (Persistence_landscape_on_grid) or not (Persistence_landscape_on_grid_exact). + This makes a big difference since mapping the intervals on the grid makes the computation time smaller but only provides an approximation of the landscape values. - Due to a lack of rigorous description of the algorithms to deal with this non--rigorous representation of persistence - landscapes in the literature, we are providing a short discussion of them in below. + Since persistence landscapes originating from persistence diagrams have slope \f$1\f$ or \f$-1\f$, we have an + estimate of a region between the grid points where the landscapes can be located. + That allows to estimate an error made when performing various operations on landscapes. Note that for average + landscapes the slope is in range \f$[-1,1]\f$ and similar estimates can be used. + + Due to the lack of rigorous description of the algorithms for these non rigorous representations of persistence + landscapes in the literature, we provide a short discussion below. Let us assume that we want to compute persistence landscape on a interval \f$[x,y]\f$. Let us assume that we want to use \f$N\f$ grid points for that purpose. @@ -166,11 +168,11 @@ namespace Persistence_representations { functions) on the i-th point of a grid, i.e. \f$x + i \frac{y-x}{N}\f$. When averaging two persistence landscapes represented by a grid we need to make sure that they are defined in a - compatible grids. I.e. the intervals \f$[x,y]\f$ on which they are defined are + compatible grids, i.e. the intervals \f$[x,y]\f$ on which they are defined are the same, and the numbers of grid points \f$N\f$ are the same in both cases. If this is the case, we simply compute - point-wise averages of the entries of corresponding - vectors (In this whole section we assume that if one vector of numbers is shorter than another, we extend the shorter - one with zeros so that they have the same length.) + point-wise averages of the entries of the corresponding + vectors (in this whole section we assume that if one vector of numbers is shorter than the other, we extend the shortest + one with zeros so that they have the same length). Computations of distances between two persistence landscapes on a grid is not much different than in the rigorous case. In this case, we sum up the distances between the same levels of @@ -179,11 +181,11 @@ namespace Persistence_representations { Similarly as in case of distance, when computing the scalar product of two persistence landscapes on a grid, we sum up the scalar products of corresponding levels of landscapes. For each level, - we assume that the persistence landscape on a grid between two grid points is approximated by linear function. - Therefore to compute scalar product of two corresponding levels of landscapes, + we assume that the persistence landscape on a grid between two grid points is approximated by a linear function. + Therefore to compute the scalar product of two corresponding levels of landscapes, we sum up the integrals of products of line segments for every pair of constitutive grid points. - Note that for this representation we need to specify a few parameters: + Note that for these representations we need to specify a few parameters: \li Begin and end point of a grid -- the interval \f$[x,y]\f$ (real numbers). \li Number of points in a grid (positive integer \f$N\f$). @@ -192,29 +194,33 @@ namespace Persistence_representations { Note that the same representation is used in TDA R-package \cite Fasy_Kim_Lecci_Maria_tda. \section sec_persistence_heat_maps Persistence heat maps + Reference manual: \ref Gudhi::Persistence_representations::Persistence_heat_maps
- This is a general class of discrete structures which are based on idea of placing a kernel in the points of - persistence diagrams. + Reference manual: \ref Gudhi::Persistence_representations::Persistence_heat_maps_exact
+ + This is a general class of discrete structures which are based on idea of placing a kernel in the points of persistence diagrams. This idea appeared in work by many authors over the last 15 years. As far as we know this idea was firstly described in the work of Bologna group in \cite Ferri_Frosini_comparision_sheme_1 and \cite Ferri_Frosini_comparision_sheme_2. Later it has been described by Colorado State University group in \cite Persistence_Images_2017. The presented paper - in the first time provide a discussion of stability of the representation. - Also, the same ideas are used in construction of two recent kernels used for machine learning: - \cite Kusano_Fukumizu_Hiraoka_PWGK and \cite Reininghaus_Huber_ALL_PSSK. Both the kernel's construction uses - interesting ideas to ensure stability of the representation with respect to Wasserstein metric. In the kernel + in the first time provided a discussion of stability of this representation. + Also, the same ideas are used in the construction of two recent kernels used for machine learning: + \cite Kusano_Fukumizu_Hiraoka_PWGK and \cite Reininghaus_Huber_ALL_PSSK. Both the kernels use + interesting ideas to ensure stability of the representations with respect to the 1-Wasserstein metric. In the kernel presented in \cite Kusano_Fukumizu_Hiraoka_PWGK, a scaling function is used to multiply the Gaussian kernel in the - way that the points close to diagonal got low weight and consequently do not have a big influence on the resulting + way that the points close to diagonal have low weights and consequently do not have a big influence on the resulting distribution. In \cite Reininghaus_Huber_ALL_PSSK for every point \f$(b,d)\f$ two Gaussian kernels are added: first, with a weight 1 in a point \f$(b,d)\f$, and the second, with the weight -1 for a point \f$(b,d)\f$. In both cases, the representations are stable with respect to 1-Wasserstein distance. - In Persistence\_representations package we currently implement a discretization of the distributions described above. - The base of this implementation is 2-dimensional array of pixels. Each pixel have assigned a real value which - is a sum of values of distributions induced by each point of the persistence diagram. At the moment we compute the - sum of values on a center of a pixels. It can be easily extended to any other function - (like for instance sum of integrals of the intermediate distribution on a pixel). + In Persistence_representations package, we currently implement a discretization of the distributions described above. + The base of this implementation is a 2-dimensional array of pixels. To each pixel is assigned a real value which + is the sum of the distribution values induced by each point of the persistence diagram. + As for Persistence_landscapes, we propose two different representations depending on whether the persistence intervals are also mapped on the pixels + (Persistence_heat_maps) or not (Persistence_heat_maps_exact). + At the moment we compute the sum over the evaluations of the distributions on the pixel centers. It can be easily extended to any other function + (like for instance the sum of the integrals of the distributions over the pixels). - The parameters that determine the structure are the following: + Concerning Persistence_heat_maps, the parameters that determine the structure are the following: \li A positive integer k determining the size of the kernel we used (we always assume that the kernels are square). \li A filter: in practice a square matrix of a size \f$2k+1 \times 2k+1\f$. By default, this is a discretization of @@ -226,6 +232,7 @@ namespace Persistence_representations { to diagonal are given then sometimes the kernel have support that reaches the region below the diagonal. If the value of this parameter is true, then the values below diagonal can be erased. + Concerning Persistence_heat_maps_exact, only Gaussian kernels are implemented, so the parameters are the array of pixels, the weight functions for the Gaussians and the bandwidth of the Gaussians. \section sec_persistence_vectors Persistence vectors Reference manual: \ref Gudhi::Persistence_representations::Vector_distances_in_diagram
diff --git a/src/Persistence_representations/example/CMakeLists.txt b/src/Persistence_representations/example/CMakeLists.txt index 89284e38..3142f19b 100644 --- a/src/Persistence_representations/example/CMakeLists.txt +++ b/src/Persistence_representations/example/CMakeLists.txt @@ -37,12 +37,12 @@ add_test(NAME Persistence_weighted_gaussian COMMAND $) install(TARGETS Persistence_weighted_gaussian DESTINATION bin) -add_executable ( Persistence_image persistence_image.cpp ) -add_test(NAME Persistence_image - COMMAND $) -install(TARGETS Persistence_image DESTINATION bin) - -add_executable ( Landscape landscape.cpp ) -add_test(NAME Landscape - COMMAND $) -install(TARGETS Landscape DESTINATION bin) +add_executable ( Persistence_heat_maps_exact persistence_heat_maps_exact.cpp ) +add_test(NAME Persistence_heat_maps_exact + COMMAND $) +install(TARGETS Persistence_heat_maps_exact DESTINATION bin) + +add_executable ( Persistence_landscape_on_grid_exact persistence_landscape_on_grid_exact.cpp ) +add_test(NAME Persistence_landscape_on_grid_exact + COMMAND $) +install(TARGETS Persistence_landscape_on_grid_exact DESTINATION bin) diff --git a/src/cython/include/Kernels_interface.h b/src/cython/include/Kernels_interface.h index 03050408..dd46656f 100644 --- a/src/cython/include/Kernels_interface.h +++ b/src/cython/include/Kernels_interface.h @@ -25,6 +25,7 @@ #include #include +#include #include #include diff --git a/src/cython/include/Vectors_interface.h b/src/cython/include/Vectors_interface.h index 7e191f2a..902ccc10 100644 --- a/src/cython/include/Vectors_interface.h +++ b/src/cython/include/Vectors_interface.h @@ -23,9 +23,9 @@ #ifndef INCLUDE_VECTORS_INTERFACE_H_ #define INCLUDE_VECTORS_INTERFACE_H_ -#include -#include -#include +#include +#include +#include #include #include @@ -38,7 +38,7 @@ namespace Gudhi { namespace persistence_diagram { std::vector > compute_ls(const std::vector >& diag, int nb_ls, double min_x, double max_x, int res_x) { - Gudhi::Persistence_representations::Landscape L(diag, nb_ls, min_x, max_x, res_x); + Gudhi::Persistence_representations::Persistence_landscape_on_grid_exact L(diag, nb_ls, min_x, max_x, res_x); return L.vectorize(); } @@ -47,7 +47,7 @@ namespace persistence_diagram { if(weight.compare("linear") == 0) weight_fn = Gudhi::Persistence_representations::linear_weight; if(weight.compare("arctan") == 0) weight_fn = Gudhi::Persistence_representations::arctan_weight(C,p); if(weight.compare("const") == 0) weight_fn = Gudhi::Persistence_representations::const_weight; - Gudhi::Persistence_representations::Persistence_image P(diag, min_x, max_x, res_x, min_y, max_y, res_y, weight_fn, sigma); + Gudhi::Persistence_representations::Persistence_heat_maps_exact P(diag, min_x, max_x, res_x, min_y, max_y, res_y, weight_fn, sigma); return P.vectorize(); } -- cgit v1.2.3 From 8fb3396a3e1aef4e4ed9752b28a19a0eccaac84e Mon Sep 17 00:00:00 2001 From: mcarrier Date: Wed, 25 Apr 2018 16:49:23 +0000 Subject: renamed landscape and image code to make them fit in Pawel's denomination git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3397 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: ddbd1dc31e12654bf3a9d9aaf67c4dfc482faa03 --- .../example/persistence_heat_maps_exact.cpp | 54 +++++++++ .../persistence_landscape_on_grid_exact.cpp | 51 +++++++++ .../include/gudhi/Persistence_heat_maps_exact.h | 125 +++++++++++++++++++++ .../gudhi/Persistence_landscape_on_grid_exact.h | 107 ++++++++++++++++++ 4 files changed, 337 insertions(+) create mode 100644 src/Persistence_representations/example/persistence_heat_maps_exact.cpp create mode 100644 src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp create mode 100644 src/Persistence_representations/include/gudhi/Persistence_heat_maps_exact.h create mode 100644 src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h diff --git a/src/Persistence_representations/example/persistence_heat_maps_exact.cpp b/src/Persistence_representations/example/persistence_heat_maps_exact.cpp new file mode 100644 index 00000000..30346d78 --- /dev/null +++ b/src/Persistence_representations/example/persistence_heat_maps_exact.cpp @@ -0,0 +1,54 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 . + */ + +#include +#include + +#include +#include +#include +#include + +using PI = Gudhi::Persistence_representations::Persistence_heat_maps_exact; +using Weight = std::function) >; + +int main(int argc, char** argv) { + + std::vector > persistence; + + persistence.push_back(std::make_pair(1, 2)); + persistence.push_back(std::make_pair(6, 8)); + persistence.push_back(std::make_pair(0, 4)); + persistence.push_back(std::make_pair(3, 8)); + + double min_x = 0.0; double max_x = 10.0; int res_x = 100; double min_y = 0.0; double max_y = 10.0; int res_y = 100; double sigma = 1.0; Weight weight = Gudhi::Persistence_representations::linear_weight; + + PI pim(persistence, min_x, max_x, res_x, min_y, max_y, res_y, weight, sigma); + std::vector > P = pim.vectorize(); + + for(int i = 0; i < res_y; i++){ + for(int j = 0; j < res_x; j++) std::cout << P[i][j] << " "; + std::cout << std::endl; + } + + return 0; +} diff --git a/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp b/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp new file mode 100644 index 00000000..29416693 --- /dev/null +++ b/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp @@ -0,0 +1,51 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 . + */ + +#include + +#include +#include +#include + +using LS = Gudhi::Persistence_representations::Persistence_landscape_on_grid_exact; + +int main(int argc, char** argv) { + + std::vector > persistence; + + persistence.push_back(std::make_pair(1, 2)); + persistence.push_back(std::make_pair(6, 8)); + persistence.push_back(std::make_pair(0, 4)); + persistence.push_back(std::make_pair(3, 8)); + + int nb_ls = 3; double min_x = 0.0; double max_x = 10.0; int res_x = 100; + + LS ls(persistence, nb_ls, min_x, max_x, res_x); + std::vector > L = ls.vectorize(); + + for(int i = 0; i < nb_ls; i++){ + for(int j = 0; j < res_x; j++) std::cout << L[i][j] << " "; + std::cout << std::endl; + } + + return 0; +} diff --git a/src/Persistence_representations/include/gudhi/Persistence_heat_maps_exact.h b/src/Persistence_representations/include/gudhi/Persistence_heat_maps_exact.h new file mode 100644 index 00000000..25f8cb47 --- /dev/null +++ b/src/Persistence_representations/include/gudhi/Persistence_heat_maps_exact.h @@ -0,0 +1,125 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 PERSISTENCE_HEAT_MAPS_EXACT_H_ +#define PERSISTENCE_HEAT_MAPS_EXACT_H_ + +// gudhi include +#include +#include +#include +#include + +// standard include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Gudhi { +namespace Persistence_representations { + +/** + * \class Persistence_heat_maps_exact gudhi/Persistence_heat_maps_exact.h + * \brief A class implementing exact persistence heat maps. + * + * \ingroup Persistence_representations + * + * \details + * + * In this class, we propose a way to approximate persistence heat maps, or persistence surfaces, by centering weighted Gaussians on each point of the persistence diagram, and evaluating these (exact) weighted Gaussian functions + * on the pixels of a 2D grid. Note that this scheme is different from the one proposed in Persistence_heat_maps, which first maps the points of the diagram to a 2D grid, and then evaluates the (approximate) weighted Gaussian functions. + * Hence, the difference is that we do not modify the diagram in this implementation, but the code can be slower to run. +**/ + +class Persistence_heat_maps_exact { + + protected: + Persistence_diagram diagram; + int res_x, res_y; + double min_x, max_x, min_y, max_y; + Weight weight; + double sigma; + + public: + + /** \brief Persistence_heat_maps_exact constructor. + * \ingroup Persistence_heat_maps_exact + * + * @param[in] _diagram persistence diagram. + * @param[in] _min_x minimum value of pixel abscissa. + * @param[in] _max_x maximum value of pixel abscissa. + * @param[in] _res_x number of pixels for the x-direction. + * @param[in] _min_y minimum value of pixel ordinate. + * @param[in] _max_y maximum value of pixel ordinate. + * @param[in] _res_y number of pixels for the y-direction. + * @param[in] _weight weight function for the Gaussians. + * @param[in] _sigma bandwidth parameter for the Gaussians. + * + */ + Persistence_heat_maps_exact(const Persistence_diagram & _diagram, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10, double _min_y = 0.0, double _max_y = 1.0, int _res_y = 10, const Weight & _weight = arctan_weight(1,1), double _sigma = 1.0){ + diagram = _diagram; min_x = _min_x; max_x = _max_x; res_x = _res_x; min_y = _min_y; max_y = _max_y; res_y = _res_y, weight = _weight; sigma = _sigma; + } + + /** \brief Computes the persistence image of a diagram. + * \ingroup Persistence_heat_maps_exact + * + */ + std::vector > vectorize() const { + std::vector > im; for(int i = 0; i < res_y; i++) im.emplace_back(); + double step_x = (max_x - min_x)/res_x; double step_y = (max_y - min_y)/res_y; + + int num_pts = diagram.size(); + + for(int i = 0; i < res_y; i++){ + double y = min_y + i*step_y; + for(int j = 0; j < res_x; j++){ + double x = min_x + j*step_x; + + double pixel_value = 0; + for(int k = 0; k < num_pts; k++){ + double px = diagram[k].first; double py = diagram[k].second; + pixel_value += weight(std::pair(px,py)) * std::exp( -((x-px)*(x-px) + (y-(py-px))*(y-(py-px))) / (2*sigma*sigma) ) / (sigma*std::sqrt(2*pi)); + } + im[i].push_back(pixel_value); + + } + } + + return im; + + } + + + + +}; // class Persistence_heat_maps_exact +} // namespace Persistence_representations +} // namespace Gudhi + +#endif // PERSISTENCE_HEAT_MAPS_EXACT_H_ diff --git a/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h b/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h new file mode 100644 index 00000000..25f71e27 --- /dev/null +++ b/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h @@ -0,0 +1,107 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 LANDSCAPE_H_ +#define LANDSCAPE_H_ + +// gudhi include +#include +#include +#include + +// standard include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Gudhi { +namespace Persistence_representations { + +/** + * \class Persistence_landscape_on_grid_exact gudhi/Persistence_landscape_on_grid_exact.h + * \brief A class implementing exact persistence landscapes by approximating them on a collection of grid points + * + * \ingroup Persistence_representations + * + * \details + * In this class, we propose a way to approximate landscapes by sampling the x-axis of the persistence diagram and evaluating the (exact) landscape functions on the sample projections onto the diagonal. Note that this is a different approximation scheme + * from the one proposed in Persistence_landscape_on_grid, which puts a grid on the diagonal, maps the persistence intervals on this grid and computes the (approximate) landscape functions on the samples. + * Hence, the difference is that we do not modify the diagram in this implementation, but the code can be slower to run. +**/ + +class Persistence_landscape_on_grid_exact { + + protected: + Persistence_diagram diagram; + int res_x, nb_ls; + double min_x, max_x; + + public: + + /** \brief Persistence_landscape_on_grid_exact constructor. + * \ingroup Persistence_landscape_on_grid_exact + * + * @param[in] _diagram persistence diagram. + * @param[in] _nb_ls number of landscape functions. + * @param[in] _min_x minimum value of samples. + * @param[in] _max_x maximum value of samples. + * @param[in] _res_x number of samples. + * + */ + Persistence_landscape_on_grid_exact(const Persistence_diagram & _diagram, int _nb_ls = 5, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10){diagram = _diagram; nb_ls = _nb_ls; min_x = _min_x; max_x = _max_x; res_x = _res_x;} + + /** \brief Computes the landscape approximation of a diagram. + * \ingroup Persistence_landscape_on_grid_exact + * + */ + std::vector > vectorize() const { + std::vector > ls; for(int i = 0; i < nb_ls; i++) ls.emplace_back(); + int num_pts = diagram.size(); double step = (max_x - min_x)/res_x; + + for(int i = 0; i < res_x; i++){ + double x = min_x + i*step; double t = x / std::sqrt(2); std::vector events; + for(int j = 0; j < num_pts; j++){ + double px = diagram[j].first; double py = diagram[j].second; + if(t >= px && t <= py){ if(t >= (px+py)/2) events.push_back(std::sqrt(2)*(py-t)); else events.push_back(std::sqrt(2)*(t-px)); } + } + + std::sort(events.begin(), events.end(), [](const double & a, const double & b){return a > b;}); int nb_events = events.size(); + for (int j = 0; j < nb_ls; j++){ if(j < nb_events) ls[j].push_back(events[j]); else ls[j].push_back(0); } + } + return ls; + } + + + + +}; // class Persistence_landscape_on_grid_exact +} // namespace Persistence_representations +} // namespace Gudhi + +#endif // LANDSCAPE_H_ -- cgit v1.2.3 From 0213b55ef43f11c15a8e56117da822e3a2731f18 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Wed, 25 Apr 2018 16:50:30 +0000 Subject: renamed landscape and image code to make them fit in Pawel's denomination git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3398 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: c57a2eb71d848df7bd677a8f3fc803b0c87d2d8d --- .../example/landscape.cpp | 51 --------- .../example/persistence_image.cpp | 54 --------- .../include/gudhi/Landscape.h | 108 ------------------ .../include/gudhi/Persistence_image.h | 126 --------------------- 4 files changed, 339 deletions(-) delete mode 100644 src/Persistence_representations/example/landscape.cpp delete mode 100644 src/Persistence_representations/example/persistence_image.cpp delete mode 100644 src/Persistence_representations/include/gudhi/Landscape.h delete mode 100644 src/Persistence_representations/include/gudhi/Persistence_image.h diff --git a/src/Persistence_representations/example/landscape.cpp b/src/Persistence_representations/example/landscape.cpp deleted file mode 100644 index 5fa84a7c..00000000 --- a/src/Persistence_representations/example/landscape.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 . - */ - -#include - -#include -#include -#include - -using LS = Gudhi::Persistence_representations::Landscape; - -int main(int argc, char** argv) { - - std::vector > persistence; - - persistence.push_back(std::make_pair(1, 2)); - persistence.push_back(std::make_pair(6, 8)); - persistence.push_back(std::make_pair(0, 4)); - persistence.push_back(std::make_pair(3, 8)); - - int nb_ls = 3; double min_x = 0.0; double max_x = 10.0; int res_x = 100; - - LS ls(persistence, nb_ls, min_x, max_x, res_x); - std::vector > L = ls.vectorize(); - - for(int i = 0; i < nb_ls; i++){ - for(int j = 0; j < res_x; j++) std::cout << L[i][j] << " "; - std::cout << std::endl; - } - - return 0; -} diff --git a/src/Persistence_representations/example/persistence_image.cpp b/src/Persistence_representations/example/persistence_image.cpp deleted file mode 100644 index cdce3bbf..00000000 --- a/src/Persistence_representations/example/persistence_image.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 . - */ - -#include -#include - -#include -#include -#include -#include - -using PI = Gudhi::Persistence_representations::Persistence_image; -using Weight = std::function) >; - -int main(int argc, char** argv) { - - std::vector > persistence; - - persistence.push_back(std::make_pair(1, 2)); - persistence.push_back(std::make_pair(6, 8)); - persistence.push_back(std::make_pair(0, 4)); - persistence.push_back(std::make_pair(3, 8)); - - double min_x = 0.0; double max_x = 10.0; int res_x = 100; double min_y = 0.0; double max_y = 10.0; int res_y = 100; double sigma = 1.0; Weight weight = Gudhi::Persistence_representations::linear_weight; - - PI pim(persistence, min_x, max_x, res_x, min_y, max_y, res_y, weight, sigma); - std::vector > P = pim.vectorize(); - - for(int i = 0; i < res_y; i++){ - for(int j = 0; j < res_x; j++) std::cout << P[i][j] << " "; - std::cout << std::endl; - } - - return 0; -} diff --git a/src/Persistence_representations/include/gudhi/Landscape.h b/src/Persistence_representations/include/gudhi/Landscape.h deleted file mode 100644 index bbbca36b..00000000 --- a/src/Persistence_representations/include/gudhi/Landscape.h +++ /dev/null @@ -1,108 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 LANDSCAPE_H_ -#define LANDSCAPE_H_ - -// gudhi include -#include -#include -#include - -// standard include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Gudhi { -namespace Persistence_representations { - -/** - * \class Landscape gudhi/Landscape.h - * \brief A class implementing landscapes. - * - * \ingroup Persistence_representations - * - * \details - * - * The landscape is a way to turn a persistence diagram into \f$L^2\f$ functions. Roughly, the idea is to see the boundaries of the rank functions as scalar functions taking values on the diagonal. - * See \cite bubenik_landscapes_2015 for more details. Here we provide a way to approximate such functions by computing their values on a set of samples. - * -**/ - -class Landscape { - - protected: - Persistence_diagram diagram; - int res_x, nb_ls; - double min_x, max_x; - - public: - - /** \brief Landscape constructor. - * \ingroup Landscape - * - * @param[in] _diagram persistence diagram. - * @param[in] _nb_ls number of landscape functions. - * @param[in] _min_x minimum value of samples. - * @param[in] _max_x maximum value of samples. - * @param[in] _res_x number of samples. - * - */ - Landscape(const Persistence_diagram & _diagram, int _nb_ls = 5, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10){diagram = _diagram; nb_ls = _nb_ls; min_x = _min_x; max_x = _max_x; res_x = _res_x;} - - /** \brief Computes the landscape of a diagram. - * \ingroup Landscape - * - */ - std::vector > vectorize() const { - std::vector > ls; for(int i = 0; i < nb_ls; i++) ls.emplace_back(); - int num_pts = diagram.size(); double step = (max_x - min_x)/res_x; - - for(int i = 0; i < res_x; i++){ - double x = min_x + i*step; double t = x / std::sqrt(2); std::vector events; - for(int j = 0; j < num_pts; j++){ - double px = diagram[j].first; double py = diagram[j].second; - if(t >= px && t <= py){ if(t >= (px+py)/2) events.push_back(std::sqrt(2)*(py-t)); else events.push_back(std::sqrt(2)*(t-px)); } - } - - std::sort(events.begin(), events.end(), [](const double & a, const double & b){return a > b;}); int nb_events = events.size(); - for (int j = 0; j < nb_ls; j++){ if(j < nb_events) ls[j].push_back(events[j]); else ls[j].push_back(0); } - } - return ls; - } - - - - -}; // class Landscape -} // namespace Persistence_representations -} // namespace Gudhi - -#endif // LANDSCAPE_H_ diff --git a/src/Persistence_representations/include/gudhi/Persistence_image.h b/src/Persistence_representations/include/gudhi/Persistence_image.h deleted file mode 100644 index 76b34d8d..00000000 --- a/src/Persistence_representations/include/gudhi/Persistence_image.h +++ /dev/null @@ -1,126 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 PERSISTENCE_IMAGE_H_ -#define PERSISTENCE_IMAGE_H_ - -// gudhi include -#include -#include -#include -#include - -// standard include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Gudhi { -namespace Persistence_representations { - -/** - * \class Persistence_image gudhi/Persistence_image.h - * \brief A class implementing the persistence images. - * - * \ingroup Persistence_representations - * - * \details - * - * Persistence images are a way to build images from persistence diagrams. Roughly, the idea is to center Gaussians on each diagram point, with a weight that usually depends on - * the distance to the diagonal, so that the diagram is turned into a function, and then to discretize the plane into pixels, and integrate this function on each pixel. - * See \cite Persistence_Images_2017 for more details. - * -**/ - -class Persistence_image { - - protected: - Persistence_diagram diagram; - int res_x, res_y; - double min_x, max_x, min_y, max_y; - Weight weight; - double sigma; - - public: - - /** \brief Persistence Image constructor. - * \ingroup Persistence_image - * - * @param[in] _diagram persistence diagram. - * @param[in] _min_x minimum value of pixel abscissa. - * @param[in] _max_x maximum value of pixel abscissa. - * @param[in] _res_x number of pixels for the x-direction. - * @param[in] _min_y minimum value of pixel ordinate. - * @param[in] _max_y maximum value of pixel ordinate. - * @param[in] _res_y number of pixels for the y-direction. - * @param[in] _weight weight function for the Gaussians. - * @param[in] _sigma bandwidth parameter for the Gaussians. - * - */ - Persistence_image(const Persistence_diagram & _diagram, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10, double _min_y = 0.0, double _max_y = 1.0, int _res_y = 10, const Weight & _weight = arctan_weight(1,1), double _sigma = 1.0){ - diagram = _diagram; min_x = _min_x; max_x = _max_x; res_x = _res_x; min_y = _min_y; max_y = _max_y; res_y = _res_y, weight = _weight; sigma = _sigma; - } - - /** \brief Computes the persistence image of a diagram. - * \ingroup Persistence_image - * - */ - std::vector > vectorize() const { - std::vector > im; for(int i = 0; i < res_y; i++) im.emplace_back(); - double step_x = (max_x - min_x)/res_x; double step_y = (max_y - min_y)/res_y; - - int num_pts = diagram.size(); - - for(int i = 0; i < res_y; i++){ - double y = min_y + i*step_y; - for(int j = 0; j < res_x; j++){ - double x = min_x + j*step_x; - - double pixel_value = 0; - for(int k = 0; k < num_pts; k++){ - double px = diagram[k].first; double py = diagram[k].second; - pixel_value += weight(std::pair(px,py)) * std::exp( -((x-px)*(x-px) + (y-(py-px))*(y-(py-px))) / (2*sigma*sigma) ) / (sigma*std::sqrt(2*pi)); - } - im[i].push_back(pixel_value); - - } - } - - return im; - - } - - - - -}; // class Persistence_image -} // namespace Persistence_representations -} // namespace Gudhi - -#endif // PERSISTENCE_IMAGE_H_ -- cgit v1.2.3 From 4ed2c5e0d99a48d242deb3318d01731cd21fd1d0 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Mon, 30 Apr 2018 12:20:54 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3408 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 6bfd16f3520497204e4bb67e0705ce120af23ec9 --- src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h index 235918fe..d8ed0d98 100644 --- a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h +++ b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h @@ -232,8 +232,8 @@ class Sliced_Wasserstein { } // Sort angles. - std::sort(angles1.begin(), angles1.end(), [=](std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);}); - std::sort(angles2.begin(), angles2.end(), [=](std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);}); + std::sort(angles1.begin(), angles1.end(), [=](const std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);}); + std::sort(angles2.begin(), angles2.end(), [=](const std::pair >& p1, const std::pair >& p2){return (p1.first < p2.first);}); // Initialize orders of the points of both PDs (given by ordinates when theta = -pi/2). std::vector orderp1, orderp2; -- cgit v1.2.3 From b2f3d32845b9e3dac752311fbd3b750d8b6ba030 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Thu, 3 May 2018 15:54:01 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3412 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 612ea7ce6d4d5d743e606f9d7b443e21b25d8034 --- .../example/persistence_heat_maps_exact.cpp | 3 ++- .../example/persistence_landscape_on_grid_exact.cpp | 3 ++- .../example/persistence_weighted_gaussian.cpp | 1 + .../example/sliced_wasserstein.cpp | 1 + .../include/gudhi/common_persistence_representations.h | 16 +++++++++------- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Persistence_representations/example/persistence_heat_maps_exact.cpp b/src/Persistence_representations/example/persistence_heat_maps_exact.cpp index 30346d78..f15b710d 100644 --- a/src/Persistence_representations/example/persistence_heat_maps_exact.cpp +++ b/src/Persistence_representations/example/persistence_heat_maps_exact.cpp @@ -28,12 +28,13 @@ #include #include +using Persistence_diagram = Gudhi::Persistence_representations::Persistence_diagram; using PI = Gudhi::Persistence_representations::Persistence_heat_maps_exact; using Weight = std::function) >; int main(int argc, char** argv) { - std::vector > persistence; + Persistence_diagram persistence; persistence.push_back(std::make_pair(1, 2)); persistence.push_back(std::make_pair(6, 8)); diff --git a/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp b/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp index 29416693..da27bc5a 100644 --- a/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp +++ b/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp @@ -26,11 +26,12 @@ #include #include +using Persistence_diagram = Gudhi::Persistence_representations::Persistence_diagram; using LS = Gudhi::Persistence_representations::Persistence_landscape_on_grid_exact; int main(int argc, char** argv) { - std::vector > persistence; + Persistence_diagram persistence; persistence.push_back(std::make_pair(1, 2)); persistence.push_back(std::make_pair(6, 8)); diff --git a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp index db60755f..7945e4f1 100644 --- a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp +++ b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp @@ -26,6 +26,7 @@ #include #include +using Persistence_diagram = Gudhi::Persistence_representations::Persistence_diagram; using PWG = Gudhi::Persistence_representations::Persistence_weighted_gaussian; int main(int argc, char** argv) { diff --git a/src/Persistence_representations/example/sliced_wasserstein.cpp b/src/Persistence_representations/example/sliced_wasserstein.cpp index d37cb23c..2104e2b2 100644 --- a/src/Persistence_representations/example/sliced_wasserstein.cpp +++ b/src/Persistence_representations/example/sliced_wasserstein.cpp @@ -26,6 +26,7 @@ #include #include +using Persistence_diagram = Gudhi::Persistence_representations::Persistence_diagram; using SW = Gudhi::Persistence_representations::Sliced_Wasserstein; int main(int argc, char** argv) { diff --git a/src/Persistence_representations/include/gudhi/common_persistence_representations.h b/src/Persistence_representations/include/gudhi/common_persistence_representations.h index 884fce58..1960e370 100644 --- a/src/Persistence_representations/include/gudhi/common_persistence_representations.h +++ b/src/Persistence_representations/include/gudhi/common_persistence_representations.h @@ -28,6 +28,15 @@ #include #include + + +namespace Gudhi { +namespace Persistence_representations { +// this file contain an implementation of some common procedures used in Persistence_representations. + +static constexpr double pi = boost::math::constants::pi(); + + /** * In this module, we use the name Persistence_diagram for the representation of a diagram in a vector of pairs of two double. */ @@ -37,13 +46,6 @@ using Persistence_diagram = std::vector >; * In this module, we use the name Weight for the representation of a function taking a pair of two double and returning a double. */ using Weight = std::function) >; - -namespace Gudhi { -namespace Persistence_representations { -// this file contain an implementation of some common procedures used in Persistence_representations. - -static constexpr double pi = boost::math::constants::pi(); - // double epsi = std::numeric_limits::epsilon(); double epsi = 0.000005; -- cgit v1.2.3 From e778857da09c25ed351b5e77dcba319ce44fdb7f Mon Sep 17 00:00:00 2001 From: mcarrier Date: Thu, 10 May 2018 05:52:21 +0000 Subject: added betti sequences git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3431 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 4680471ce781468c247c40fb5e6eb699dce890c8 --- .../example/CMakeLists.txt | 5 ++ .../example/betti_sequence.cpp | 49 +++++++++++ .../persistence_landscape_on_grid_exact.cpp | 4 +- .../include/gudhi/Betti_sequence.h | 95 ++++++++++++++++++++++ .../gudhi/Persistence_landscape_on_grid_exact.h | 25 +++--- 5 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 src/Persistence_representations/example/betti_sequence.cpp create mode 100644 src/Persistence_representations/include/gudhi/Betti_sequence.h diff --git a/src/Persistence_representations/example/CMakeLists.txt b/src/Persistence_representations/example/CMakeLists.txt index 3142f19b..9be22085 100644 --- a/src/Persistence_representations/example/CMakeLists.txt +++ b/src/Persistence_representations/example/CMakeLists.txt @@ -46,3 +46,8 @@ add_executable ( Persistence_landscape_on_grid_exact persistence_landscape_on_gr add_test(NAME Persistence_landscape_on_grid_exact COMMAND $) install(TARGETS Persistence_landscape_on_grid_exact DESTINATION bin) + +add_executable ( Betti_sequence betti_sequence.cpp ) +add_test(NAME Betti_sequence + COMMAND $) +install(TARGETS Betti_sequence DESTINATION bin) diff --git a/src/Persistence_representations/example/betti_sequence.cpp b/src/Persistence_representations/example/betti_sequence.cpp new file mode 100644 index 00000000..a422a822 --- /dev/null +++ b/src/Persistence_representations/example/betti_sequence.cpp @@ -0,0 +1,49 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 . + */ + +#include + +#include +#include +#include + +using Persistence_diagram = Gudhi::Persistence_representations::Persistence_diagram; +using BS = Gudhi::Persistence_representations::Betti_sequence; + +int main(int argc, char** argv) { + + Persistence_diagram persistence; + + persistence.push_back(std::make_pair(1, 2)); + persistence.push_back(std::make_pair(6, 8)); + persistence.push_back(std::make_pair(0, 4)); + persistence.push_back(std::make_pair(3, 8)); + + double min_x = 0; double max_x = 8; int res_x = 1000; + + BS bs(persistence, min_x, max_x, res_x); + std::vector B = bs.vectorize(); + + for(int i = 0; i < res_x; i++) std::cout << B[i] << ", "; + + return 0; +} diff --git a/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp b/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp index da27bc5a..9ce42649 100644 --- a/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp +++ b/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp @@ -38,13 +38,13 @@ int main(int argc, char** argv) { persistence.push_back(std::make_pair(0, 4)); persistence.push_back(std::make_pair(3, 8)); - int nb_ls = 3; double min_x = 0.0; double max_x = 10.0; int res_x = 100; + int nb_ls = 2; double min_x = 0; double max_x = 8; int res_x = 1000; LS ls(persistence, nb_ls, min_x, max_x, res_x); std::vector > L = ls.vectorize(); for(int i = 0; i < nb_ls; i++){ - for(int j = 0; j < res_x; j++) std::cout << L[i][j] << " "; + for(int j = 0; j < res_x; j++) std::cout << L[i][j] << ", "; std::cout << std::endl; } diff --git a/src/Persistence_representations/include/gudhi/Betti_sequence.h b/src/Persistence_representations/include/gudhi/Betti_sequence.h new file mode 100644 index 00000000..57c52ad2 --- /dev/null +++ b/src/Persistence_representations/include/gudhi/Betti_sequence.h @@ -0,0 +1,95 @@ +/* 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): Mathieu Carriere + * + * Copyright (C) 2018 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 BETTI_SEQUENCE_H_ +#define BETTI_SEQUENCE_H_ + +// gudhi include +#include +#include +#include + +// standard include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Gudhi { +namespace Persistence_representations { + +/** + * \class Betti_sequence gudhi/Betti_sequence.h + * \brief A class implementing Betti sequences + * + * \ingroup Persistence_representations + * + * \details +**/ + +class Betti_sequence { + + protected: + Persistence_diagram diagram; + int res_x, nb_cv; + double min_x, max_x; + + public: + + /** \brief Betti_sequence constructor. + * \ingroup Betti_sequence + * + * @param[in] _diagram persistence diagram. + * @param[in] _min_x minimum value of samples. + * @param[in] _max_x maximum value of samples. + * @param[in] _res_x number of samples. + * + */ + Betti_sequence(const Persistence_diagram & _diagram, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10){diagram = _diagram; min_x = _min_x; max_x = _max_x; res_x = _res_x;} + + /** \brief Computes the Betti sequences of a diagram. + * \ingroup Betti_sequence + * + */ + std::vector vectorize() const { + int num_pts = diagram.size(); double step = (max_x - min_x)/(res_x - 1); + std::vector bs(res_x); for(int i = 0; i < res_x; i++) bs[i] = 0; + for(int j = 0; j < num_pts; j++){ + double px = diagram[j].first; double py = diagram[j].second; + int first = std::ceil((px-min_x)/step); int last = std::ceil((py-min_x)/step); + for(int i = first; i < last; i++) bs[i] += 1; + } + + return bs; + } + +}; // class Betti_sequence +} // namespace Persistence_representations +} // namespace Gudhi + +#endif // BETTI_SEQUENCE_H_ diff --git a/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h b/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h index 25f71e27..52f24195 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h +++ b/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h @@ -82,24 +82,25 @@ class Persistence_landscape_on_grid_exact { */ std::vector > vectorize() const { std::vector > ls; for(int i = 0; i < nb_ls; i++) ls.emplace_back(); - int num_pts = diagram.size(); double step = (max_x - min_x)/res_x; + int num_pts = diagram.size(); double step = (max_x - min_x)/(res_x - 1); + + std::vector > ls_t; for(int i = 0; i < res_x; i++) ls_t.emplace_back(); + for(int j = 0; j < num_pts; j++){ + double px = diagram[j].first; double py = diagram[j].second; double mid = (px+py)/2; + int first = std::ceil((px-min_x)/step); int middle = std::ceil((mid-min_x)/step); int last = std::ceil((py-min_x)/step); double x = min_x + first*step; + for(int i = first; i < middle; i++){ double value = std::sqrt(2)*(x-px); ls_t[i].push_back(value); x += step; } + for(int i = middle; i < last; i++){ double value = std::sqrt(2)*(py-x); ls_t[i].push_back(value); x += step; } + } for(int i = 0; i < res_x; i++){ - double x = min_x + i*step; double t = x / std::sqrt(2); std::vector events; - for(int j = 0; j < num_pts; j++){ - double px = diagram[j].first; double py = diagram[j].second; - if(t >= px && t <= py){ if(t >= (px+py)/2) events.push_back(std::sqrt(2)*(py-t)); else events.push_back(std::sqrt(2)*(t-px)); } - } - - std::sort(events.begin(), events.end(), [](const double & a, const double & b){return a > b;}); int nb_events = events.size(); - for (int j = 0; j < nb_ls; j++){ if(j < nb_events) ls[j].push_back(events[j]); else ls[j].push_back(0); } + std::sort(ls_t[i].begin(), ls_t[i].end(), [](const double & a, const double & b){return a > b;}); + int nb_events_i = ls_t[i].size(); + for (int j = 0; j < nb_ls; j++){ if(j < nb_events_i) ls[j].push_back(ls_t[i][j]); else ls[j].push_back(0); } } + return ls; } - - - }; // class Persistence_landscape_on_grid_exact } // namespace Persistence_representations } // namespace Gudhi -- cgit v1.2.3 From 9b3f3e610646b9a2d35369bdb7a6f272e816eb34 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Tue, 29 May 2018 08:30:12 +0000 Subject: minor change git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3480 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: f14f9a0909174ba3569560d00c02d92360b8c0e5 --- .../include/gudhi/Persistence_weighted_gaussian.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h index 76c43e65..9ef47bf1 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h +++ b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h @@ -39,6 +39,7 @@ #include #include #include +#include namespace Gudhi { namespace Persistence_representations { -- cgit v1.2.3 From 10c6f6be72a2631cd1a1d28ed61343d55bd2b759 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Thu, 14 Jun 2018 09:25:16 +0000 Subject: small modif on PWGK git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3612 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: c0b8c70acfbf1a7f4b7bddb69a161086fb249c76 --- src/cython/cython/kernels.pyx | 26 ++++++++++++++------------ src/cython/cython/vectors.pyx | 3 +++ src/cython/include/Kernels_interface.h | 21 +++++++++++++-------- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/cython/cython/kernels.pyx b/src/cython/cython/kernels.pyx index 0cb296ec..cb8fc0fd 100644 --- a/src/cython/cython/kernels.pyx +++ b/src/cython/cython/kernels.pyx @@ -34,8 +34,8 @@ cdef extern from "Kernels_interface.h" namespace "Gudhi::persistence_diagram": vector[vector[double]] sw_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) double pss (vector[pair[double, double]], vector[pair[double, double]], double, int) vector[vector[double]] pss_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) - double pwg (vector[pair[double, double]], vector[pair[double, double]], double, int, double, double) - vector[vector[double]] pwg_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int, double, double) + double pwg (vector[pair[double, double]], vector[pair[double, double]], int, string, double, double, double) + vector[vector[double]] pwg_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], int, string, double, double, double) def sliced_wasserstein(diagram_1, diagram_2, sigma = 1, N = 100): """ @@ -65,37 +65,39 @@ def sliced_wasserstein_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100): """ return sw_matrix(diagrams_1, diagrams_2, sigma, N) -def persistence_weighted_gaussian(diagram_1, diagram_2, sigma = 1, N = 100, C = 1, p = 1): +def persistence_weighted_gaussian(diagram_1, diagram_2, N = 100, weight = "arctan", sigma = 1.0, C = 1.0, p = 1.0): """ :param diagram_1: The first diagram. :type diagram_1: vector[pair[double, double]] :param diagram_2: The second diagram. :type diagram_2: vector[pair[double, double]] - :param sigma: bandwidth of Gaussian :param N: number of Fourier features - :param C: cost of persistence weight - :param p: power of persistence weight + :param weight: weight to use for the diagram points + :param sigma: bandwidth of Gaussian + :param C: cost of arctan persistence weight + :param p: power of arctan persistence weight :returns: the persistence weighted gaussian kernel. """ - return pwg(diagram_1, diagram_2, sigma, N, C, p) + return pwg(diagram_1, diagram_2, N, weight, sigma, C, p) -def persistence_weighted_gaussian_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100, C = 1, p = 1): +def persistence_weighted_gaussian_matrix(diagrams_1, diagrams_2, N = 100, weight = "arctan", sigma = 1.0, C = 1.0, p = 1.0): """ :param diagram_1: The first set of diagrams. :type diagram_1: vector[vector[pair[double, double]]] :param diagram_2: The second set of diagrams. :type diagram_2: vector[vector[pair[double, double]]] - :param sigma: bandwidth of Gaussian :param N: number of Fourier features - :param C: cost of persistence weight - :param p: power of persistence weight + :param weight: weight to use for the diagram points + :param sigma: bandwidth of Gaussian + :param C: cost of arctan persistence weight + :param p: power of arctan persistence weight :returns: the persistence weighted gaussian kernel matrix. """ - return pwg_matrix(diagrams_1, diagrams_2, sigma, N, C, p) + return pwg_matrix(diagrams_1, diagrams_2, N, weight, sigma, C, p) def persistence_scale_space(diagram_1, diagram_2, sigma = 1, N = 100): """ diff --git a/src/cython/cython/vectors.pyx b/src/cython/cython/vectors.pyx index 42390ae6..af53f739 100644 --- a/src/cython/cython/vectors.pyx +++ b/src/cython/cython/vectors.pyx @@ -58,7 +58,10 @@ def persistence_image(diagram, min_x = 0.0, max_x = 1.0, res_x = 10, min_y = 0.0 :param min_x: Minimum ordinate :param max_x: Maximum ordinate :param res_x: Number of ordinate pixels + :param weight: Weight to use for the diagram points :param sigma: bandwidth of Gaussian + :param C: cost of arctan persistence weight + :param p: power of arctan persistence weight :returns: the persistence image """ diff --git a/src/cython/include/Kernels_interface.h b/src/cython/include/Kernels_interface.h index dd46656f..a07d7820 100644 --- a/src/cython/include/Kernels_interface.h +++ b/src/cython/include/Kernels_interface.h @@ -23,6 +23,7 @@ #ifndef INCLUDE_KERNELS_INTERFACE_H_ #define INCLUDE_KERNELS_INTERFACE_H_ +#include #include #include #include @@ -46,9 +47,13 @@ namespace persistence_diagram { return sw1.compute_scalar_product(sw2); } - double pwg(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N, double C, double p) { - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, sigma, N, Gudhi::Persistence_representations::arctan_weight(C,p)); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, sigma, N, Gudhi::Persistence_representations::arctan_weight(C,p)); + double pwg(const std::vector>& diag1, const std::vector>& diag2, int N, std::string weight, double sigma, double C, double p) { + Gudhi::Persistence_representations::Weight weight_fn; + if(weight.compare("linear") == 0) weight_fn = Gudhi::Persistence_representations::linear_weight; + if(weight.compare("arctan") == 0) weight_fn = Gudhi::Persistence_representations::arctan_weight(C,p); + if(weight.compare("const") == 0) weight_fn = Gudhi::Persistence_representations::const_weight; + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, sigma, N, weight_fn); + Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, sigma, N, weight_fn); return pwg1.compute_scalar_product(pwg2); } @@ -87,11 +92,11 @@ namespace persistence_diagram { return matrix; } - std::vector > pwg_matrix(const std::vector > >& s1, const std::vector > >& s2, double sigma, int N, double C, double p){ + std::vector > pwg_matrix(const std::vector > >& s1, const std::vector > >& s2, int N, std::string weight, double sigma, double C, double p){ std::vector > matrix; int num_diag_1 = s1.size(); int num_diag_2 = s2.size(); for(int i = 0; i < num_diag_1; i++){ std::cout << 100.0*i/num_diag_1 << " %" << std::endl; - std::vector ps; for(int j = 0; j < num_diag_2; j++) ps.push_back(pwg(s1[i], s2[j], sigma, N, C, p)); matrix.push_back(ps); + std::vector ps; for(int j = 0; j < num_diag_2; j++) ps.push_back(pwg(s1[i], s2[j], N, weight, sigma, C, p)); matrix.push_back(ps); } return matrix; } @@ -99,13 +104,13 @@ namespace persistence_diagram { std::vector > pss_matrix(const std::vector > >& s1, const std::vector > >& s2, double sigma, int N){ std::vector > > ss1, ss2; std::vector > matrix; int num_diag_1 = s1.size(); int num_diag_2 = s2.size(); for(int i = 0; i < num_diag_1; i++){ - std::vector> pd1 = s1[i]; int numpts = s1[i].size(); + std::vector> pd1 = s1[i]; int numpts = s1[i].size(); for(int j = 0; j < numpts; j++) pd1.emplace_back(s1[i][j].second,s1[i][j].first); ss1.push_back(pd1); } - + for(int i = 0; i < num_diag_2; i++){ - std::vector> pd2 = s2[i]; int numpts = s2[i].size(); + std::vector> pd2 = s2[i]; int numpts = s2[i].size(); for(int j = 0; j < numpts; j++) pd2.emplace_back(s2[i][j].second,s2[i][j].first); ss2.push_back(pd2); } -- cgit v1.2.3 From 0741c3eabbfece1c73ac76aa44adbe2904b6124d Mon Sep 17 00:00:00 2001 From: mcarrier Date: Sat, 23 Jun 2018 04:59:39 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3628 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 12f32a1c8ca31e7e0a40e1c3502e2a3d810d5bc5 --- .../doc/Persistence_representations_doc.h | 131 ++++++--------- .../example/CMakeLists.txt | 20 --- .../example/betti_sequence.cpp | 49 ------ .../example/persistence_heat_maps.cpp | 12 ++ .../example/persistence_heat_maps_exact.cpp | 55 ------- .../persistence_landscape_on_grid_exact.cpp | 52 ------ .../example/persistence_weighted_gaussian.cpp | 99 ----------- .../include/gudhi/Betti_sequence.h | 95 ----------- .../include/gudhi/Persistence_heat_maps.h | 174 ++++++++++++++++---- .../include/gudhi/Persistence_heat_maps_exact.h | 125 -------------- .../include/gudhi/Persistence_landscape_on_grid.h | 6 +- .../gudhi/Persistence_landscape_on_grid_exact.h | 108 ------------ .../include/gudhi/Persistence_weighted_gaussian.h | 182 --------------------- .../include/gudhi/Sliced_Wasserstein.h | 6 +- .../include/gudhi/Weight_functions.h | 81 --------- .../gudhi/common_persistence_representations.h | 15 +- src/cython/cython/kernels.pyx | 128 --------------- src/cython/cython/vectors.pyx | 68 -------- src/cython/include/Kernels_interface.h | 130 --------------- src/cython/include/Vectors_interface.h | 59 ------- 20 files changed, 217 insertions(+), 1378 deletions(-) delete mode 100644 src/Persistence_representations/example/betti_sequence.cpp delete mode 100644 src/Persistence_representations/example/persistence_heat_maps_exact.cpp delete mode 100644 src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp delete mode 100644 src/Persistence_representations/example/persistence_weighted_gaussian.cpp delete mode 100644 src/Persistence_representations/include/gudhi/Betti_sequence.h delete mode 100644 src/Persistence_representations/include/gudhi/Persistence_heat_maps_exact.h delete mode 100644 src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h delete mode 100644 src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h delete mode 100644 src/Persistence_representations/include/gudhi/Weight_functions.h delete mode 100644 src/cython/cython/kernels.pyx delete mode 100644 src/cython/cython/vectors.pyx delete mode 100644 src/cython/include/Kernels_interface.h delete mode 100644 src/cython/include/Vectors_interface.h diff --git a/src/Persistence_representations/doc/Persistence_representations_doc.h b/src/Persistence_representations/doc/Persistence_representations_doc.h index d0b02739..73800d0d 100644 --- a/src/Persistence_representations/doc/Persistence_representations_doc.h +++ b/src/Persistence_representations/doc/Persistence_representations_doc.h @@ -24,6 +24,7 @@ #define DOC_GUDHI_STAT_H_ namespace Gudhi { + namespace Persistence_representations { /** \defgroup Persistence_representations Persistence representations @@ -128,35 +129,33 @@ namespace Persistence_representations { function \f$L : \mathbb{N} \times \mathbb{R} \to [0,\infty)\f$ of two variables, if we define \f$L(k,t) = \lambda_k(t)\f$. - The detailed description of algorithms used to compute persistence landscapes can be found in \cite bubenik_dlotko_landscapes_2016. - Note that this implementation provides exact representation of landscapes. That have many advantages, but also a few drawbacks. - For instance, as discussed in \cite bubenik_dlotko_landscapes_2016, the exact representation of landscape may be of quadratic size with respect - to the input persistence diagram. It may therefore happen that, for very large diagrams, using this representation may be memory--prohibitive. - In such a case, there are two possible ways to proceed: + The detailed description of algorithms used to compute persistence landscapes can be found in + \cite bubenik_dlotko_landscapes_2016. + Note that this implementation provides exact representation of landscapes. That have many advantages, but also a few + drawbacks. For instance, as discussed + in \cite bubenik_dlotko_landscapes_2016, the exact representation of landscape may be of quadratic size with respect + to the input persistence diagram. It may therefore happen + that, for very large diagrams, using this representation may be memory--prohibitive. In such a case, there are two + possible ways to proceed: - \li Use representation on a grid---see section \ref sec_landscapes_on_grid. + \li Use non exact representation on a grid described in the Section \ref sec_landscapes_on_grid. \li Compute just a number of initial nonzero landscapes. This option is available from C++ level as a last parameter of the constructor of persistence landscape (set by default to std::numeric_limits::max()). \section sec_landscapes_on_grid Persistence Landscapes on a grid - Reference manual: \ref Gudhi::Persistence_representations::Persistence_landscape_on_grid
- Reference manual: \ref Gudhi::Persistence_representations::Persistence_landscape_on_grid_exact
- - Here, we provide alternative, not exact, representations of persistence landscapes defined in Section \ref sec_persistence_landscapes. - Unlike Section \ref sec_persistence_landscapes, we build representations of persistence landscapes by evaluating the landscape functions on a finite, equally distributed grid of points. - We propose two different representations depending on whether the persistence intervals are also mapped on the grid (Persistence_landscape_on_grid) or not (Persistence_landscape_on_grid_exact). - This makes a big difference since mapping the intervals on the grid makes the computation time smaller but only provides an approximation of the landscape values. + This is an alternative, not--exact, representation of persistence landscapes defined in the Section \ref + sec_persistence_landscapes. Unlike in the Section \ref sec_persistence_landscapes we build a + representation of persistence landscape by sampling its values on a finite, equally distributed grid of points. + Since, the persistence landscapes that originate from persistence diagrams have slope \f$1\f$ or \f$-1\f$, we have an + estimate of a region between the grid points where the landscape cab be located. + That allows to estimate an error make when performing various operations on landscape. Note that for average + landscapes the slope is in range \f$[-1,1]\f$ and similar estimate can be used. - Since persistence landscapes originating from persistence diagrams have slope \f$1\f$ or \f$-1\f$, we have an - estimate of a region between the grid points where the landscapes can be located. - That allows to estimate an error made when performing various operations on landscapes. Note that for average - landscapes the slope is in range \f$[-1,1]\f$ and similar estimates can be used. - - Due to the lack of rigorous description of the algorithms for these non rigorous representations of persistence - landscapes in the literature, we provide a short discussion below. + Due to a lack of rigorous description of the algorithms to deal with this non--rigorous representation of persistence + landscapes in the literature, we are providing a short discussion of them in below. Let us assume that we want to compute persistence landscape on a interval \f$[x,y]\f$. Let us assume that we want to use \f$N\f$ grid points for that purpose. @@ -168,11 +167,11 @@ namespace Persistence_representations { functions) on the i-th point of a grid, i.e. \f$x + i \frac{y-x}{N}\f$. When averaging two persistence landscapes represented by a grid we need to make sure that they are defined in a - compatible grids, i.e. the intervals \f$[x,y]\f$ on which they are defined are + compatible grids. I.e. the intervals \f$[x,y]\f$ on which they are defined are the same, and the numbers of grid points \f$N\f$ are the same in both cases. If this is the case, we simply compute - point-wise averages of the entries of the corresponding - vectors (in this whole section we assume that if one vector of numbers is shorter than the other, we extend the shortest - one with zeros so that they have the same length). + point-wise averages of the entries of corresponding + vectors (In this whole section we assume that if one vector of numbers is shorter than another, we extend the shorter + one with zeros so that they have the same length.) Computations of distances between two persistence landscapes on a grid is not much different than in the rigorous case. In this case, we sum up the distances between the same levels of @@ -181,11 +180,11 @@ namespace Persistence_representations { Similarly as in case of distance, when computing the scalar product of two persistence landscapes on a grid, we sum up the scalar products of corresponding levels of landscapes. For each level, - we assume that the persistence landscape on a grid between two grid points is approximated by a linear function. - Therefore to compute the scalar product of two corresponding levels of landscapes, + we assume that the persistence landscape on a grid between two grid points is approximated by linear function. + Therefore to compute scalar product of two corresponding levels of landscapes, we sum up the integrals of products of line segments for every pair of constitutive grid points. - Note that for these representations we need to specify a few parameters: + Note that for this representation we need to specify a few parameters: \li Begin and end point of a grid -- the interval \f$[x,y]\f$ (real numbers). \li Number of points in a grid (positive integer \f$N\f$). @@ -194,33 +193,29 @@ namespace Persistence_representations { Note that the same representation is used in TDA R-package \cite Fasy_Kim_Lecci_Maria_tda. \section sec_persistence_heat_maps Persistence heat maps - Reference manual: \ref Gudhi::Persistence_representations::Persistence_heat_maps
- Reference manual: \ref Gudhi::Persistence_representations::Persistence_heat_maps_exact
- - This is a general class of discrete structures which are based on idea of placing a kernel in the points of persistence diagrams. + This is a general class of discrete structures which are based on idea of placing a kernel in the points of + persistence diagrams. This idea appeared in work by many authors over the last 15 years. As far as we know this idea was firstly described in the work of Bologna group in \cite Ferri_Frosini_comparision_sheme_1 and \cite Ferri_Frosini_comparision_sheme_2. Later it has been described by Colorado State University group in \cite Persistence_Images_2017. The presented paper - in the first time provided a discussion of stability of this representation. - Also, the same ideas are used in the construction of two recent kernels used for machine learning: - \cite Kusano_Fukumizu_Hiraoka_PWGK and \cite Reininghaus_Huber_ALL_PSSK. Both the kernels use - interesting ideas to ensure stability of the representations with respect to the 1-Wasserstein metric. In the kernel + in the first time provide a discussion of stability of the representation. + Also, the same ideas are used in construction of two recent kernels used for machine learning: + \cite Kusano_Fukumizu_Hiraoka_PWGK and \cite Reininghaus_Huber_ALL_PSSK. Both the kernel's construction uses + interesting ideas to ensure stability of the representation with respect to Wasserstein metric. In the kernel presented in \cite Kusano_Fukumizu_Hiraoka_PWGK, a scaling function is used to multiply the Gaussian kernel in the - way that the points close to diagonal have low weights and consequently do not have a big influence on the resulting + way that the points close to diagonal got low weight and consequently do not have a big influence on the resulting distribution. In \cite Reininghaus_Huber_ALL_PSSK for every point \f$(b,d)\f$ two Gaussian kernels are added: first, with a weight 1 in a point \f$(b,d)\f$, and the second, with the weight -1 for a point \f$(b,d)\f$. In both cases, the representations are stable with respect to 1-Wasserstein distance. - In Persistence_representations package, we currently implement a discretization of the distributions described above. - The base of this implementation is a 2-dimensional array of pixels. To each pixel is assigned a real value which - is the sum of the distribution values induced by each point of the persistence diagram. - As for Persistence_landscapes, we propose two different representations depending on whether the persistence intervals are also mapped on the pixels - (Persistence_heat_maps) or not (Persistence_heat_maps_exact). - At the moment we compute the sum over the evaluations of the distributions on the pixel centers. It can be easily extended to any other function - (like for instance the sum of the integrals of the distributions over the pixels). + In Persistence\_representations package we currently implement a discretization of the distributions described above. + The base of this implementation is 2-dimensional array of pixels. Each pixel have assigned a real value which + is a sum of values of distributions induced by each point of the persistence diagram. At the moment we compute the + sum of values on a center of a pixels. It can be easily extended to any other function + (like for instance sum of integrals of the intermediate distribution on a pixel). - Concerning Persistence_heat_maps, the parameters that determine the structure are the following: + The parameters that determine the structure are the following: \li A positive integer k determining the size of the kernel we used (we always assume that the kernels are square). \li A filter: in practice a square matrix of a size \f$2k+1 \times 2k+1\f$. By default, this is a discretization of @@ -232,7 +227,6 @@ namespace Persistence_representations { to diagonal are given then sometimes the kernel have support that reaches the region below the diagonal. If the value of this parameter is true, then the values below diagonal can be erased. - Concerning Persistence_heat_maps_exact, only Gaussian kernels are implemented, so the parameters are the array of pixels, the weight functions for the Gaussians and the bandwidth of the Gaussians. \section sec_persistence_vectors Persistence vectors Reference manual: \ref Gudhi::Persistence_representations::Vector_distances_in_diagram
@@ -256,11 +250,7 @@ namespace Persistence_representations { absolute value of differences between coordinates. A scalar product is a sum of products of values at the corresponding positions of two vectors. - - - - -\section sec_persistence_kernels Kernels on persistence diagrams + \section sec_persistence_kernels Kernels on persistence diagrams Reference manual: \ref Gudhi::Persistence_representations::Sliced_Wasserstein
Reference manual: \ref Gudhi::Persistence_representations::Persistence_weighted_gaussian
@@ -269,53 +259,26 @@ namespace Persistence_representations { between images of these pairs under a map \f$\Phi\f$ taking values in a specific (possibly non Euclidean) Hilbert space \f$k(D_i, D_j) = \langle \Phi(D_i),\Phi(D_j)\rangle\f$. Reciprocally, classical results of learning theory ensure that such a \f$\Phi\f$ exists for a given similarity function \f$k\f$ if and only if \f$k\f$ is positive semi-definite. Kernels are designed for algorithms that can be kernelized, i.e., algorithms that only require to know scalar products between instances in order to run. - Examples of such algorithms include Support Vector Machines, Principal Component Analysis and Ridge Regression. + Examples of such algorithms include Support Vector Machines, Principal Component Analysis and Ridge Regression. There have been several attempts at defining kernels, i.e., positive semi-definite functions, between persistence diagrams within the last few years. We provide implementation - for three of them: - - \li the Persistence Scale Space Kernel---see \cite Reininghaus_Huber_ALL_PSSK, which is the classical scalar product between \f$L^2\f$ functions, where persistence diagrams - are turned into functions by centering and summing Gaussian functions over the diagram points and their symmetric counterparts w.r.t. the diagonal: \f$k(D_1,D_2)=\int \Phi(D_1)\Phi(D_2)\f$, - where \f$\Phi(D)=\sum_{p\in D} {\rm exp}\left(-\frac{\|p-\cdot\|_2^2}{2\sigma^2}\right)\f$. - - \li the Persistence Weighted Gaussian Kernel---see \cite Kusano_Fukumizu_Hiraoka_PWGK, which is a slight generalization of the previous kernel, is the scalar product between - weighted Kernel Mean Embeddings of persistence diagrams w.r.t. the Gaussian Kernel \f$k_G\f$ (with corresponding map \f$\Phi_G\f$) in \f$\mathbb{R}^2\f$: - \f$k(D_1,D_2)=\langle\sum_{p\in D_1} w(p)\Phi_G(p), \sum_{q\in D_2} w(q)\Phi_G(q)\rangle\f$ - - \li the Sliced Wasserstein Kernel---see \cite pmlr-v70-carriere17a, which takes the form of a Gaussian kernel with a specific distance between persistence diagrams - called the Sliced Wasserstein Distance: \f$k(D_1,D_2)={\rm exp}\left(-\frac{SW(D_1,D_2)}{2\sigma^2}\right)\f$ + for the Sliced Wasserstein Kernel---see \cite pmlr-v70-carriere17a, which takes the form of a Gaussian kernel with a specific distance between persistence diagrams + called the Sliced Wasserstein Distance: \f$k(D_1,D_2)={\rm exp}\left(-\frac{SW(D_1,D_2)}{2\sigma^2}\right)\f$. Other kernels such as the Persistence Weighted Gaussian Kernel or + the Persistence Scale Space Kernel are implemented in Persistence_heat_maps. When launching: \code $> ./Sliced_Wasserstein \endcode - + the program output is: - + \code $> Approx SW distance: 5.33648 $> Exact SW distance: 5.33798 $> Approx SW kernel: 0.0693743 $> Exact SW kernel: 0.0693224 $> Distance induced by approx SW kernel: 1.36428 - $> Distance induced by exact SW kernel: 1.3643 - \endcode - - - and when launching: - - \code $> ./Persistence_weighted_gaussian - \endcode - - the program output is: - - \code $> Approx PWG kernel: 1.21509 - $> Exact PWG kernel: 1.13628 - $> Distance induced by approx PWG kernel: 3.23354 - $> Distance induced by exact PWG kernel: 3.25697 - $> Approx Gaussian PWG kernel: 0.0194222 - $> Exact Gaussian PWG kernel: 0.0192524 - $> Approx PSS kernel: 0.134413 - $> Exact PSS kernel: 0.133394 + $> Distance induced by exact SW kernel: 1.3643 \endcode */ diff --git a/src/Persistence_representations/example/CMakeLists.txt b/src/Persistence_representations/example/CMakeLists.txt index 9be22085..d236c3a6 100644 --- a/src/Persistence_representations/example/CMakeLists.txt +++ b/src/Persistence_representations/example/CMakeLists.txt @@ -31,23 +31,3 @@ add_executable ( Sliced_Wasserstein sliced_wasserstein.cpp ) add_test(NAME Sliced_Wasserstein COMMAND $) install(TARGETS Sliced_Wasserstein DESTINATION bin) - -add_executable ( Persistence_weighted_gaussian persistence_weighted_gaussian.cpp ) -add_test(NAME Persistence_weighted_gaussian - COMMAND $) -install(TARGETS Persistence_weighted_gaussian DESTINATION bin) - -add_executable ( Persistence_heat_maps_exact persistence_heat_maps_exact.cpp ) -add_test(NAME Persistence_heat_maps_exact - COMMAND $) -install(TARGETS Persistence_heat_maps_exact DESTINATION bin) - -add_executable ( Persistence_landscape_on_grid_exact persistence_landscape_on_grid_exact.cpp ) -add_test(NAME Persistence_landscape_on_grid_exact - COMMAND $) -install(TARGETS Persistence_landscape_on_grid_exact DESTINATION bin) - -add_executable ( Betti_sequence betti_sequence.cpp ) -add_test(NAME Betti_sequence - COMMAND $) -install(TARGETS Betti_sequence DESTINATION bin) diff --git a/src/Persistence_representations/example/betti_sequence.cpp b/src/Persistence_representations/example/betti_sequence.cpp deleted file mode 100644 index a422a822..00000000 --- a/src/Persistence_representations/example/betti_sequence.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 . - */ - -#include - -#include -#include -#include - -using Persistence_diagram = Gudhi::Persistence_representations::Persistence_diagram; -using BS = Gudhi::Persistence_representations::Betti_sequence; - -int main(int argc, char** argv) { - - Persistence_diagram persistence; - - persistence.push_back(std::make_pair(1, 2)); - persistence.push_back(std::make_pair(6, 8)); - persistence.push_back(std::make_pair(0, 4)); - persistence.push_back(std::make_pair(3, 8)); - - double min_x = 0; double max_x = 8; int res_x = 1000; - - BS bs(persistence, min_x, max_x, res_x); - std::vector B = bs.vectorize(); - - for(int i = 0; i < res_x; i++) std::cout << B[i] << ", "; - - return 0; -} diff --git a/src/Persistence_representations/example/persistence_heat_maps.cpp b/src/Persistence_representations/example/persistence_heat_maps.cpp index 323b57e9..f1791e97 100644 --- a/src/Persistence_representations/example/persistence_heat_maps.cpp +++ b/src/Persistence_representations/example/persistence_heat_maps.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -76,5 +77,16 @@ int main(int argc, char** argv) { // to compute scalar product of hm1 and hm2: std::cout << "Scalar product is : " << hm1.compute_scalar_product(hm2) << std::endl; + Gudhi::Persistence_representations::Kernel k = Gudhi::Persistence_representations::Gaussian_kernel(1.0); + + Persistence_heat_maps hm1k(persistence1, k); + Persistence_heat_maps hm2k(persistence2, k); + + Persistence_heat_maps hm1i(persistence1, 20, 20, 0, 11, 0, 11, k); + Persistence_heat_maps hm2i(persistence2, 20, 20, 0, 11, 0, 11, k); + + std::cout << "Scalar product computed with exact kernel is : " << hm1i.compute_scalar_product(hm2i) << std::endl; + std::cout << "Kernel value between PDs seen as functions is : " << hm1k.compute_scalar_product(hm2k) << std::endl; + return 0; } diff --git a/src/Persistence_representations/example/persistence_heat_maps_exact.cpp b/src/Persistence_representations/example/persistence_heat_maps_exact.cpp deleted file mode 100644 index f15b710d..00000000 --- a/src/Persistence_representations/example/persistence_heat_maps_exact.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 . - */ - -#include -#include - -#include -#include -#include -#include - -using Persistence_diagram = Gudhi::Persistence_representations::Persistence_diagram; -using PI = Gudhi::Persistence_representations::Persistence_heat_maps_exact; -using Weight = std::function) >; - -int main(int argc, char** argv) { - - Persistence_diagram persistence; - - persistence.push_back(std::make_pair(1, 2)); - persistence.push_back(std::make_pair(6, 8)); - persistence.push_back(std::make_pair(0, 4)); - persistence.push_back(std::make_pair(3, 8)); - - double min_x = 0.0; double max_x = 10.0; int res_x = 100; double min_y = 0.0; double max_y = 10.0; int res_y = 100; double sigma = 1.0; Weight weight = Gudhi::Persistence_representations::linear_weight; - - PI pim(persistence, min_x, max_x, res_x, min_y, max_y, res_y, weight, sigma); - std::vector > P = pim.vectorize(); - - for(int i = 0; i < res_y; i++){ - for(int j = 0; j < res_x; j++) std::cout << P[i][j] << " "; - std::cout << std::endl; - } - - return 0; -} diff --git a/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp b/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp deleted file mode 100644 index 9ce42649..00000000 --- a/src/Persistence_representations/example/persistence_landscape_on_grid_exact.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 . - */ - -#include - -#include -#include -#include - -using Persistence_diagram = Gudhi::Persistence_representations::Persistence_diagram; -using LS = Gudhi::Persistence_representations::Persistence_landscape_on_grid_exact; - -int main(int argc, char** argv) { - - Persistence_diagram persistence; - - persistence.push_back(std::make_pair(1, 2)); - persistence.push_back(std::make_pair(6, 8)); - persistence.push_back(std::make_pair(0, 4)); - persistence.push_back(std::make_pair(3, 8)); - - int nb_ls = 2; double min_x = 0; double max_x = 8; int res_x = 1000; - - LS ls(persistence, nb_ls, min_x, max_x, res_x); - std::vector > L = ls.vectorize(); - - for(int i = 0; i < nb_ls; i++){ - for(int j = 0; j < res_x; j++) std::cout << L[i][j] << ", "; - std::cout << std::endl; - } - - return 0; -} diff --git a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp b/src/Persistence_representations/example/persistence_weighted_gaussian.cpp deleted file mode 100644 index 7945e4f1..00000000 --- a/src/Persistence_representations/example/persistence_weighted_gaussian.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 . - */ - -#include - -#include -#include -#include - -using Persistence_diagram = Gudhi::Persistence_representations::Persistence_diagram; -using PWG = Gudhi::Persistence_representations::Persistence_weighted_gaussian; - -int main(int argc, char** argv) { - - Persistence_diagram persistence1, persistence2; - - persistence1.push_back(std::make_pair(1, 2)); - persistence1.push_back(std::make_pair(6, 8)); - persistence1.push_back(std::make_pair(0, 4)); - persistence1.push_back(std::make_pair(3, 8)); - - persistence2.push_back(std::make_pair(2, 9)); - persistence2.push_back(std::make_pair(1, 6)); - persistence2.push_back(std::make_pair(3, 5)); - persistence2.push_back(std::make_pair(6, 10)); - - double sigma = 1; - double tau = 1; - int m = 10000; - - PWG PWG1(persistence1, sigma, m, Gudhi::Persistence_representations::arctan_weight(1,1)); - PWG PWG2(persistence2, sigma, m, Gudhi::Persistence_representations::arctan_weight(1,1)); - - PWG PWGex1(persistence1, sigma, -1, Gudhi::Persistence_representations::arctan_weight(1,1)); - PWG PWGex2(persistence2, sigma, -1, Gudhi::Persistence_representations::arctan_weight(1,1)); - - - // Linear PWG - - std::cout << "Approx PWG kernel: " << PWG1.compute_scalar_product (PWG2) << std::endl; - std::cout << "Exact PWG kernel: " << PWGex1.compute_scalar_product (PWGex2) << std::endl; - - std::cout << "Distance induced by approx PWG kernel: " << PWG1.distance (PWG2) << std::endl; - std::cout << "Distance induced by exact PWG kernel: " << PWGex1.distance (PWGex2) << std::endl; - - - - - - - - // Gaussian PWG - - std::cout << "Approx Gaussian PWG kernel: " << std::exp( -PWG1.distance (PWG2) ) / (2*tau*tau) << std::endl; - std::cout << "Exact Gaussian PWG kernel: " << std::exp( -PWGex1.distance (PWGex2) ) / (2*tau*tau) << std::endl; - - - - - - - - // PSS - - Persistence_diagram pd1 = persistence1; int numpts = persistence1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(persistence1[i].second,persistence1[i].first); - Persistence_diagram pd2 = persistence2; numpts = persistence2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(persistence2[i].second,persistence2[i].first); - - PWG pwg1(pd1, 2*std::sqrt(sigma), m, Gudhi::Persistence_representations::pss_weight); - PWG pwg2(pd2, 2*std::sqrt(sigma), m, Gudhi::Persistence_representations::pss_weight); - - PWG pwgex1(pd1, 2*std::sqrt(sigma), -1, Gudhi::Persistence_representations::pss_weight); - PWG pwgex2(pd2, 2*std::sqrt(sigma), -1, Gudhi::Persistence_representations::pss_weight); - - std::cout << "Approx PSS kernel: " << pwg1.compute_scalar_product (pwg2) / (16*Gudhi::Persistence_representations::pi*sigma) << std::endl; - std::cout << "Exact PSS kernel: " << pwgex1.compute_scalar_product (pwgex2) / (16*Gudhi::Persistence_representations::pi*sigma) << std::endl; - - - - return 0; -} diff --git a/src/Persistence_representations/include/gudhi/Betti_sequence.h b/src/Persistence_representations/include/gudhi/Betti_sequence.h deleted file mode 100644 index 57c52ad2..00000000 --- a/src/Persistence_representations/include/gudhi/Betti_sequence.h +++ /dev/null @@ -1,95 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 BETTI_SEQUENCE_H_ -#define BETTI_SEQUENCE_H_ - -// gudhi include -#include -#include -#include - -// standard include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Gudhi { -namespace Persistence_representations { - -/** - * \class Betti_sequence gudhi/Betti_sequence.h - * \brief A class implementing Betti sequences - * - * \ingroup Persistence_representations - * - * \details -**/ - -class Betti_sequence { - - protected: - Persistence_diagram diagram; - int res_x, nb_cv; - double min_x, max_x; - - public: - - /** \brief Betti_sequence constructor. - * \ingroup Betti_sequence - * - * @param[in] _diagram persistence diagram. - * @param[in] _min_x minimum value of samples. - * @param[in] _max_x maximum value of samples. - * @param[in] _res_x number of samples. - * - */ - Betti_sequence(const Persistence_diagram & _diagram, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10){diagram = _diagram; min_x = _min_x; max_x = _max_x; res_x = _res_x;} - - /** \brief Computes the Betti sequences of a diagram. - * \ingroup Betti_sequence - * - */ - std::vector vectorize() const { - int num_pts = diagram.size(); double step = (max_x - min_x)/(res_x - 1); - std::vector bs(res_x); for(int i = 0; i < res_x; i++) bs[i] = 0; - for(int j = 0; j < num_pts; j++){ - double px = diagram[j].first; double py = diagram[j].second; - int first = std::ceil((px-min_x)/step); int last = std::ceil((py-min_x)/step); - for(int i = first; i < last; i++) bs[i] += 1; - } - - return bs; - } - -}; // class Betti_sequence -} // namespace Persistence_representations -} // namespace Gudhi - -#endif // BETTI_SEQUENCE_H_ diff --git a/src/Persistence_representations/include/gudhi/Persistence_heat_maps.h b/src/Persistence_representations/include/gudhi/Persistence_heat_maps.h index 35e51e63..63c6e239 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_heat_maps.h +++ b/src/Persistence_representations/include/gudhi/Persistence_heat_maps.h @@ -244,6 +244,20 @@ class Persistence_heat_maps { double max_ = std::numeric_limits::max(), unsigned dimension = std::numeric_limits::max()); + /** + * Construction that takes as inputs (1) the diagram, (2) grid parameters (min, max and number of samples for x and y axes), and (3) a universal kernel on the plane used + * to turn the diagram into a function. + **/ + Persistence_heat_maps(const Persistence_diagram & interval, size_t number_of_x_pixels, size_t number_of_y_pixels, + double min_x = 0, double max_x = 1, double min_y = 0, double max_y = 1, const Kernel & kernel = Gaussian_kernel(1.0)); + + /** + * Construction that takes as inputs (1) the diagram and (2) a universal kernel on the plane used + * to turn the diagram into a function. Note that this construction is infinite dimensional so + * only compute_scalar_product() method is valid after calling this constructor. + **/ + Persistence_heat_maps(const Persistence_diagram & interval, const Kernel & kernel = Gaussian_kernel(1.0)); + /** * Compute a mean value of a collection of heat maps and store it in the current object. Note that all the persistence *maps send in a vector to this procedure need to have the same parameters. @@ -512,15 +526,27 @@ class Persistence_heat_maps { size_t number_of_functions_for_projections_to_reals; void construct(const std::vector >& intervals_, std::vector > filter = create_Gaussian_filter(5, 1), - bool erase_below_diagonal = false, size_t number_of_pixels = 1000, double min_ = std::numeric_limits::max(), double max_ = std::numeric_limits::max()); + void construct_image_from_exact_universal_kernel(const Persistence_diagram & interval, + size_t number_of_x_pixels = 10, size_t number_of_y_pixels = 10, + double min_x = 0, double max_x = 1, double min_y = 0, double max_y = 1, const Kernel & kernel = Gaussian_kernel(1.0)); + void construct_kernel_from_exact_universal_kernel(const Persistence_diagram & interval, const Kernel & kernel = Gaussian_kernel(1.0)); + void set_up_parameters_for_basic_classes() { this->number_of_functions_for_vectorization = 1; this->number_of_functions_for_projections_to_reals = 1; } + // Boolean indicating if we are computing persistence image (true) or persistence weighted gaussian kernel (false) + bool discrete = true; + + // PWGK + Kernel k; + Persistence_diagram d; + std::vector weights; + // data Scalling_of_kernels f; bool erase_below_diagonal; @@ -529,6 +555,59 @@ class Persistence_heat_maps { std::vector > heat_map; }; +template +void Persistence_heat_maps::construct_image_from_exact_universal_kernel(const Persistence_diagram & diagram, + size_t number_of_x_pixels, size_t number_of_y_pixels, + double min_x, double max_x, + double min_y, double max_y, const Kernel & kernel) { + + this->discrete = true; Scalling_of_kernels f; this->f = f; this->min_ = min_x; this->max_ = max_x; + for(size_t i = 0; i < number_of_y_pixels; i++) this->heat_map.emplace_back(); + double step_x = (max_x - min_x)/(number_of_x_pixels - 1); double step_y = (max_y - min_y)/(number_of_y_pixels - 1); + + int num_pts = diagram.size(); + + for(size_t i = 0; i < number_of_y_pixels; i++){ + double y = min_y + i*step_y; + for(size_t j = 0; j < number_of_x_pixels; j++){ + double x = min_x + j*step_x; + + std::pair grid_point(x,y); double pixel_value = 0; + for(int k = 0; k < num_pts; k++){ + double px = diagram[k].first; double py = diagram[k].second; std::pair diagram_point(px,py); + pixel_value += this->f(diagram_point) * kernel(diagram_point, grid_point); + } + this->heat_map[i].push_back(pixel_value); + + } + } + +} + + +template +Persistence_heat_maps::Persistence_heat_maps(const Persistence_diagram & diagram, + size_t number_of_x_pixels, size_t number_of_y_pixels, + double min_x, double max_x, + double min_y, double max_y, const Kernel & kernel) { + this->construct_image_from_exact_universal_kernel(diagram, number_of_x_pixels, number_of_y_pixels, min_x, max_x, min_y, max_y, kernel); + this->set_up_parameters_for_basic_classes(); +} + +template +void Persistence_heat_maps::construct_kernel_from_exact_universal_kernel(const Persistence_diagram & diagram, const Kernel & kernel){ + this->discrete = false; Scalling_of_kernels f; this->f = f; this->k = kernel; this->d = diagram; + int num_pts = this->d.size(); + for (int i = 0; i < num_pts; i++) this->weights.push_back(this->f(this->d[i])); +} + + +template +Persistence_heat_maps::Persistence_heat_maps(const Persistence_diagram& diagram, const Kernel & kernel) { + this->construct_kernel_from_exact_universal_kernel(diagram, kernel); + this->set_up_parameters_for_basic_classes(); +} + // if min_ == max_, then the program is requested to set up the values itself based on persistence intervals template void Persistence_heat_maps::construct(const std::vector >& intervals_, @@ -826,13 +905,16 @@ void Persistence_heat_maps::load_from_file(const char* file // Concretizations of virtual methods: template std::vector Persistence_heat_maps::vectorize(int number_of_function) const { + + std::vector result; + if(!discrete){std::cout << "No vectorize method in case of infinite dimensional vectorization" << std::endl; return result;} + // convert this->heat_map into one large vector: size_t size_of_result = 0; for (size_t i = 0; i != this->heat_map.size(); ++i) { size_of_result += this->heat_map[i].size(); } - std::vector result; result.reserve(size_of_result); for (size_t i = 0; i != this->heat_map.size(); ++i) { @@ -846,34 +928,39 @@ std::vector Persistence_heat_maps::vectorize(int nu template double Persistence_heat_maps::distance(const Persistence_heat_maps& second, double power) const { - // first we need to check if (*this) and second are defined on the same domain and have the same dimensions: - if (!this->check_if_the_same(second)) { - std::cerr << "The persistence images are of non compatible sizes. We cannot therefore compute distance between " - "them. The program will now terminate"; - throw "The persistence images are of non compatible sizes. The program will now terminate"; - } + if(this->discrete){ + // first we need to check if (*this) and second are defined on the same domain and have the same dimensions: + if (!this->check_if_the_same(second)) { + std::cerr << "The persistence images are of non compatible sizes. We cannot therefore compute distance between " + "them. The program will now terminate"; + throw "The persistence images are of non compatible sizes. The program will now terminate"; + } - // if we are here, we know that the two persistence images are defined on the same domain, so we can start computing - // their distances: + // if we are here, we know that the two persistence images are defined on the same domain, so we can start computing their distances: - double distance = 0; - if (power < std::numeric_limits::max()) { - for (size_t i = 0; i != this->heat_map.size(); ++i) { - for (size_t j = 0; j != this->heat_map[i].size(); ++j) { - distance += pow(fabs(this->heat_map[i][j] - second.heat_map[i][j]), power); + double distance = 0; + if (power < std::numeric_limits::max()) { + for (size_t i = 0; i != this->heat_map.size(); ++i) { + for (size_t j = 0; j != this->heat_map[i].size(); ++j) { + distance += pow(fabs(this->heat_map[i][j] - second.heat_map[i][j]), power); + } } - } - } else { - // in this case, we compute max norm distance - for (size_t i = 0; i != this->heat_map.size(); ++i) { - for (size_t j = 0; j != this->heat_map[i].size(); ++j) { - if (distance < fabs(this->heat_map[i][j] - second.heat_map[i][j])) { - distance = fabs(this->heat_map[i][j] - second.heat_map[i][j]); + } else { + // in this case, we compute max norm distance + for (size_t i = 0; i != this->heat_map.size(); ++i) { + for (size_t j = 0; j != this->heat_map[i].size(); ++j) { + if (distance < fabs(this->heat_map[i][j] - second.heat_map[i][j])) { + distance = fabs(this->heat_map[i][j] - second.heat_map[i][j]); + } } } } + return distance; + } else { + + return std::sqrt(this->compute_scalar_product(*this) + second.compute_scalar_product(second) -2 * this->compute_scalar_product(second)); + } - return distance; } template @@ -895,22 +982,37 @@ void Persistence_heat_maps::compute_average( template double Persistence_heat_maps::compute_scalar_product(const Persistence_heat_maps& second) const { - // first we need to check if (*this) and second are defined on the same domain and have the same dimensions: - if (!this->check_if_the_same(second)) { - std::cerr << "The persistence images are of non compatible sizes. We cannot therefore compute distance between " - "them. The program will now terminate"; - throw "The persistence images are of non compatible sizes. The program will now terminate"; - } - // if we are here, we know that the two persistence images are defined on the same domain, so we can start computing - // their scalar product: - double scalar_prod = 0; - for (size_t i = 0; i != this->heat_map.size(); ++i) { - for (size_t j = 0; j != this->heat_map[i].size(); ++j) { - scalar_prod += this->heat_map[i][j] * second.heat_map[i][j]; + if(discrete){ + // first we need to check if (*this) and second are defined on the same domain and have the same dimensions: + if (!this->check_if_the_same(second)) { + std::cerr << "The persistence images are of non compatible sizes. We cannot therefore compute distance between " + "them. The program will now terminate"; + throw "The persistence images are of non compatible sizes. The program will now terminate"; } + + // if we are here, we know that the two persistence images are defined on the same domain, so we can start computing + // their scalar product: + double scalar_prod = 0; + for (size_t i = 0; i != this->heat_map.size(); ++i) { + for (size_t j = 0; j != this->heat_map[i].size(); ++j) { + scalar_prod += this->heat_map[i][j] * second.heat_map[i][j]; + } + } + return scalar_prod; } - return scalar_prod; + + else{ + GUDHI_CHECK(this->approx != second.approx || this->f != second.f, std::invalid_argument("Error: different values for representations")); + + int num_pts1 = this->d.size(); int num_pts2 = second.d.size(); double kernel_val = 0; + for(int i = 0; i < num_pts1; i++) + for(int j = 0; j < num_pts2; j++) + kernel_val += this->weights[i] * second.weights[j] * this->k(this->d[i], second.d[j]); + return kernel_val; + } + + } } // namespace Persistence_representations diff --git a/src/Persistence_representations/include/gudhi/Persistence_heat_maps_exact.h b/src/Persistence_representations/include/gudhi/Persistence_heat_maps_exact.h deleted file mode 100644 index 7c5b2fdc..00000000 --- a/src/Persistence_representations/include/gudhi/Persistence_heat_maps_exact.h +++ /dev/null @@ -1,125 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 PERSISTENCE_HEAT_MAPS_EXACT_H_ -#define PERSISTENCE_HEAT_MAPS_EXACT_H_ - -// gudhi include -#include -#include -#include -#include - -// standard include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Gudhi { -namespace Persistence_representations { - -/** - * \class Persistence_heat_maps_exact gudhi/Persistence_heat_maps_exact.h - * \brief A class implementing exact persistence heat maps. - * - * \ingroup Persistence_representations - * - * \details - * - * In this class, we propose a way to approximate persistence heat maps, or persistence surfaces, by centering weighted Gaussians on each point of the persistence diagram, and evaluating these (exact) weighted Gaussian functions - * on the pixels of a 2D grid. Note that this scheme is different from the one proposed in Persistence_heat_maps, which first maps the points of the diagram to a 2D grid, and then evaluates the (approximate) weighted Gaussian functions. - * Hence, the difference is that we do not modify the diagram in this implementation, but the code can be slower to run. -**/ - -class Persistence_heat_maps_exact { - - protected: - Persistence_diagram diagram; - int res_x, res_y; - double min_x, max_x, min_y, max_y; - Weight weight; - double sigma; - - public: - - /** \brief Persistence_heat_maps_exact constructor. - * \ingroup Persistence_heat_maps_exact - * - * @param[in] _diagram persistence diagram. - * @param[in] _min_x minimum value of pixel abscissa. - * @param[in] _max_x maximum value of pixel abscissa. - * @param[in] _res_x number of pixels for the x-direction. - * @param[in] _min_y minimum value of pixel ordinate. - * @param[in] _max_y maximum value of pixel ordinate. - * @param[in] _res_y number of pixels for the y-direction. - * @param[in] _weight weight function for the Gaussians. - * @param[in] _sigma bandwidth parameter for the Gaussians. - * - */ - Persistence_heat_maps_exact(const Persistence_diagram & _diagram, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10, double _min_y = 0.0, double _max_y = 1.0, int _res_y = 10, const Weight & _weight = arctan_weight(1,1), double _sigma = 1.0){ - diagram = _diagram; min_x = _min_x; max_x = _max_x; res_x = _res_x; min_y = _min_y; max_y = _max_y; res_y = _res_y, weight = _weight; sigma = _sigma; - } - - /** \brief Computes the persistence image of a diagram. - * \ingroup Persistence_heat_maps_exact - * - */ - std::vector > vectorize() const { - std::vector > im; for(int i = 0; i < res_y; i++) im.emplace_back(); - double step_x = (max_x - min_x)/(res_x - 1); double step_y = (max_y - min_y)/(res_y - 1); - - int num_pts = diagram.size(); - - for(int i = 0; i < res_y; i++){ - double y = min_y + i*step_y; - for(int j = 0; j < res_x; j++){ - double x = min_x + j*step_x; - - double pixel_value = 0; - for(int k = 0; k < num_pts; k++){ - double px = diagram[k].first; double py = diagram[k].second; - pixel_value += weight(std::pair(px,py)) * std::exp( -((x-px)*(x-px) + (y-(py-px))*(y-(py-px))) / (2*sigma*sigma) ) / (sigma*std::sqrt(2*pi)); - } - im[i].push_back(pixel_value); - - } - } - - return im; - - } - - - - -}; // class Persistence_heat_maps_exact -} // namespace Persistence_representations -} // namespace Gudhi - -#endif // PERSISTENCE_HEAT_MAPS_EXACT_H_ diff --git a/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid.h b/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid.h index fd8a181c..db0e362a 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid.h +++ b/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid.h @@ -986,7 +986,7 @@ void Persistence_landscape_on_grid::set_up_values_of_landscapes(const std::vecto for (size_t int_no = 0; int_no != p.size(); ++int_no) { size_t grid_interval_begin = (p[int_no].first - grid_min_) / dx; size_t grid_interval_end = (p[int_no].second - grid_min_) / dx; - size_t grid_interval_midpoint = (size_t)(0.5 * (grid_interval_begin + grid_interval_end)); + size_t grid_interval_midpoint = (size_t)(0.5 * (p[int_no].first + p[int_no].second) - grid_min + 1); if (dbg) { std::cerr << "Considering an interval : " << p[int_no].first << "," << p[int_no].second << std::endl; @@ -996,7 +996,7 @@ void Persistence_landscape_on_grid::set_up_values_of_landscapes(const std::vecto std::cerr << "grid_interval_midpoint : " << grid_interval_midpoint << std::endl; } - double landscape_value = dx; + double landscape_value = grid_min + dx * (grid_interval_begin + 1) - p[int_no].first; for (size_t i = grid_interval_begin + 1; i < grid_interval_midpoint; ++i) { if (dbg) { std::cerr << "Adding landscape value (going up) for a point : " << i << " equal : " << landscape_value @@ -1030,6 +1030,8 @@ void Persistence_landscape_on_grid::set_up_values_of_landscapes(const std::vecto } landscape_value += dx; } + + landscape_value = p[int_no].second - grid_min - dx * grid_interval_midpoint; for (size_t i = grid_interval_midpoint; i <= grid_interval_end; ++i) { if (landscape_value > 0) { if (number_of_levels != std::numeric_limits::max()) { diff --git a/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h b/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h deleted file mode 100644 index 52f24195..00000000 --- a/src/Persistence_representations/include/gudhi/Persistence_landscape_on_grid_exact.h +++ /dev/null @@ -1,108 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 LANDSCAPE_H_ -#define LANDSCAPE_H_ - -// gudhi include -#include -#include -#include - -// standard include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Gudhi { -namespace Persistence_representations { - -/** - * \class Persistence_landscape_on_grid_exact gudhi/Persistence_landscape_on_grid_exact.h - * \brief A class implementing exact persistence landscapes by approximating them on a collection of grid points - * - * \ingroup Persistence_representations - * - * \details - * In this class, we propose a way to approximate landscapes by sampling the x-axis of the persistence diagram and evaluating the (exact) landscape functions on the sample projections onto the diagonal. Note that this is a different approximation scheme - * from the one proposed in Persistence_landscape_on_grid, which puts a grid on the diagonal, maps the persistence intervals on this grid and computes the (approximate) landscape functions on the samples. - * Hence, the difference is that we do not modify the diagram in this implementation, but the code can be slower to run. -**/ - -class Persistence_landscape_on_grid_exact { - - protected: - Persistence_diagram diagram; - int res_x, nb_ls; - double min_x, max_x; - - public: - - /** \brief Persistence_landscape_on_grid_exact constructor. - * \ingroup Persistence_landscape_on_grid_exact - * - * @param[in] _diagram persistence diagram. - * @param[in] _nb_ls number of landscape functions. - * @param[in] _min_x minimum value of samples. - * @param[in] _max_x maximum value of samples. - * @param[in] _res_x number of samples. - * - */ - Persistence_landscape_on_grid_exact(const Persistence_diagram & _diagram, int _nb_ls = 5, double _min_x = 0.0, double _max_x = 1.0, int _res_x = 10){diagram = _diagram; nb_ls = _nb_ls; min_x = _min_x; max_x = _max_x; res_x = _res_x;} - - /** \brief Computes the landscape approximation of a diagram. - * \ingroup Persistence_landscape_on_grid_exact - * - */ - std::vector > vectorize() const { - std::vector > ls; for(int i = 0; i < nb_ls; i++) ls.emplace_back(); - int num_pts = diagram.size(); double step = (max_x - min_x)/(res_x - 1); - - std::vector > ls_t; for(int i = 0; i < res_x; i++) ls_t.emplace_back(); - for(int j = 0; j < num_pts; j++){ - double px = diagram[j].first; double py = diagram[j].second; double mid = (px+py)/2; - int first = std::ceil((px-min_x)/step); int middle = std::ceil((mid-min_x)/step); int last = std::ceil((py-min_x)/step); double x = min_x + first*step; - for(int i = first; i < middle; i++){ double value = std::sqrt(2)*(x-px); ls_t[i].push_back(value); x += step; } - for(int i = middle; i < last; i++){ double value = std::sqrt(2)*(py-x); ls_t[i].push_back(value); x += step; } - } - - for(int i = 0; i < res_x; i++){ - std::sort(ls_t[i].begin(), ls_t[i].end(), [](const double & a, const double & b){return a > b;}); - int nb_events_i = ls_t[i].size(); - for (int j = 0; j < nb_ls; j++){ if(j < nb_events_i) ls[j].push_back(ls_t[i][j]); else ls[j].push_back(0); } - } - - return ls; - } - -}; // class Persistence_landscape_on_grid_exact -} // namespace Persistence_representations -} // namespace Gudhi - -#endif // LANDSCAPE_H_ diff --git a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h b/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h deleted file mode 100644 index 9ef47bf1..00000000 --- a/src/Persistence_representations/include/gudhi/Persistence_weighted_gaussian.h +++ /dev/null @@ -1,182 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 PERSISTENCE_WEIGHTED_GAUSSIAN_H_ -#define PERSISTENCE_WEIGHTED_GAUSSIAN_H_ - -// gudhi include -#include -#include -#include - -// standard include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Gudhi { -namespace Persistence_representations { -/** - * \class Persistence_weighted_gaussian gudhi/Persistence_weighted_gaussian.h - * \brief A class implementing the Persistence Weighted Gaussian kernel and a specific case thereof called the Persistence Scale Space kernel. - * - * \ingroup Persistence_representations - * - * \details - * The Persistence Weighted Gaussian kernel is built with Gaussian Kernel Mean Embedding, meaning that each persistence diagram is first - * sent to the Hilbert space of a Gaussian kernel with bandwidth parameter \f$\sigma >0\f$ using a weighted mean embedding \f$\Phi\f$: - * - * \f$ \Phi\,:\,D\,\rightarrow\,\sum_{p\in D}\,w(p)\,{\rm exp}\left(-\frac{\|p-\cdot\|_2^2}{2\sigma^2}\right) \f$, - * - * Usually, the weight function is chosen to be an arctan function of the distance of the point to the diagonal: - * \f$w(p) = {\rm arctan}(C\,|y-x|^\alpha)\f$, for some parameters \f$C,\alpha >0\f$. - * Then, their scalar product in this space is computed: - * - * \f$ k(D_1,D_2)=\langle\Phi(D_1),\Phi(D_2)\rangle - * \,=\,\sum_{p\in D_1}\,\sum_{q\in D_2}\,w(p)\,w(q)\,{\rm exp}\left(-\frac{\|p-q\|_2^2}{2\sigma^2}\right).\f$ - * - * Note that one may apply a second Gaussian kernel to their distance in this space and still get a kernel. - * - * It follows that the computation time is \f$O(n^2)\f$ where \f$n\f$ is the number of points - * in the diagrams. This time can be improved by computing approximations of the kernel - * with \f$m\f$ Fourier features \cite Rahimi07randomfeatures. In that case, the computation time becomes \f$O(mn)\f$. - * - * The Persistence Scale Space kernel is a Persistence Weighted Gaussian kernel between modified diagrams: - * the symmetric of each point with respect to the diagonal is first added in each diagram, and then the weight function - * is set to be +1 if the point is above the diagonal and -1 otherwise. - * - * For more details, please see \cite Kusano_Fukumizu_Hiraoka_PWGK - * and \cite Reininghaus_Huber_ALL_PSSK . - * -**/ -class Persistence_weighted_gaussian{ - - protected: - Persistence_diagram diagram; - Weight weight; - double sigma; - int approx; - - public: - - /** \brief Persistence Weighted Gaussian kernel constructor. - * \ingroup Persistence_weighted_gaussian - * - * @param[in] _diagram persistence diagram. - * @param[in] _sigma bandwidth parameter of the Gaussian kernel used for the Kernel Mean Embedding of the diagrams. - * @param[in] _approx number of random Fourier features in case of approximate computation, set to -1 for exact computation. - * @param[in] _weight weight function for the points in the diagrams. - * - */ - Persistence_weighted_gaussian(const Persistence_diagram & _diagram, double _sigma = 1.0, int _approx = 1000, const Weight & _weight = arctan_weight(1,1)){diagram = _diagram; sigma = _sigma; approx = _approx; weight = _weight;} - - - // ********************************** - // Utils. - // ********************************** - - std::vector > Fourier_feat(const Persistence_diagram & diag, const std::vector > & z, const Weight & weight = arctan_weight(1,1)) const { - int md = diag.size(); std::vector > b; int mz = z.size(); - for(int i = 0; i < mz; i++){ - double d1 = 0; double d2 = 0; double zx = z[i].first; double zy = z[i].second; - for(int j = 0; j < md; j++){ - double x = diag[j].first; double y = diag[j].second; - d1 += weight(diag[j])*cos(x*zx + y*zy); - d2 += weight(diag[j])*sin(x*zx + y*zy); - } - b.emplace_back(d1,d2); - } - return b; - } - - std::vector > random_Fourier(double sigma, int m = 1000) const { - std::normal_distribution distrib(0,1); std::vector > z; std::random_device rd; - for(int i = 0; i < m; i++){ - std::mt19937 e1(rd()); std::mt19937 e2(rd()); - double zx = distrib(e1); double zy = distrib(e2); - z.emplace_back(zx/sigma,zy/sigma); - } - return z; - } - - - - // ********************************** - // Scalar product + distance. - // ********************************** - - /** \brief Evaluation of the kernel on a pair of diagrams. - * \ingroup Persistence_weighted_gaussian - * - * @pre sigma, approx and weight attributes need to be the same for both instances. - * @param[in] second other instance of class Persistence_weighted_gaussian. - * - */ - double compute_scalar_product(const Persistence_weighted_gaussian & second) const { - - GUDHI_CHECK(this->sigma != second.sigma || this->approx != second.approx || this->weight != second.weight, std::invalid_argument("Error: different values for representations")); - Persistence_diagram diagram1 = this->diagram; Persistence_diagram diagram2 = second.diagram; - - if(this->approx == -1){ - int num_pts1 = diagram1.size(); int num_pts2 = diagram2.size(); double k = 0; - for(int i = 0; i < num_pts1; i++) - for(int j = 0; j < num_pts2; j++) - k += this->weight(diagram1[i])*this->weight(diagram2[j])*exp(-((diagram1[i].first - diagram2[j].first) * (diagram1[i].first - diagram2[j].first) + - (diagram1[i].second - diagram2[j].second) * (diagram1[i].second - diagram2[j].second)) - /(2*this->sigma*this->sigma)); - return k; - } - else{ - std::vector > z = random_Fourier(this->sigma, this->approx); - std::vector > b1 = Fourier_feat(diagram1,z,this->weight); - std::vector > b2 = Fourier_feat(diagram2,z,this->weight); - double d = 0; for(int i = 0; i < this->approx; i++) d += b1[i].first*b2[i].first + b1[i].second*b2[i].second; - return d/this->approx; - } - } - - /** \brief Evaluation of the distance between images of diagrams in the Hilbert space of the kernel. - * \ingroup Persistence_weighted_gaussian - * - * @pre sigma, approx and weight attributes need to be the same for both instances. - * @param[in] second other instance of class Persistence_weighted_gaussian. - * - */ - double distance(const Persistence_weighted_gaussian & second) const { - GUDHI_CHECK(this->sigma != second.sigma || this->approx != second.approx || this->weight != second.weight, std::invalid_argument("Error: different values for representations")); - return std::pow(this->compute_scalar_product(*this) + second.compute_scalar_product(second)-2*this->compute_scalar_product(second), 0.5); - } - - -}; // class Persistence_weighted_gaussian -} // namespace Persistence_representations -} // namespace Gudhi - -#endif // PERSISTENCE_WEIGHTED_GAUSSIAN_H_ diff --git a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h index d8ed0d98..8c92ab54 100644 --- a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h +++ b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h @@ -62,7 +62,7 @@ namespace Persistence_representations { * in the diagrams, or approximated by sampling \f$N\f$ lines in the circle in \f$O(Nn{\rm log}(n))\f$ time. The Sliced Wasserstein Kernel is then computed as: * * \f$ k(D_1,D_2) = {\rm exp}\left(-\frac{SW(D_1,D_2)}{2\sigma^2}\right).\f$ - * + * * For more details, please see \cite pmlr-v70-carriere17a . * **/ @@ -80,7 +80,7 @@ class Sliced_Wasserstein { void build_rep(){ if(approx > 0){ - + double step = pi/this->approx; int n = diagram.size(); @@ -188,7 +188,7 @@ class Sliced_Wasserstein { * \ingroup Sliced_Wasserstein * * @pre approx attribute needs to be the same for both instances. - * @param[in] second other instance of class Sliced_Wasserstein. + * @param[in] second other instance of class Sliced_Wasserstein. * * */ diff --git a/src/Persistence_representations/include/gudhi/Weight_functions.h b/src/Persistence_representations/include/gudhi/Weight_functions.h deleted file mode 100644 index 78de406d..00000000 --- a/src/Persistence_representations/include/gudhi/Weight_functions.h +++ /dev/null @@ -1,81 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 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 WEIGHT_FUNCTIONS_H_ -#define WEIGHT_FUNCTIONS_H_ - -// gudhi include -#include -#include - -// standard include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Gudhi { -namespace Persistence_representations { - -/** \fn static double pss_weight(std::pair p) - * \brief Persistence Scale Space kernel weight function. - * \ingroup Persistence_representations - * - * @param[in] p point in 2D. - */ -static double pss_weight(std::pair p) {if(p.second > p.first) return 1; else return -1;} - -/** \fn static double linear_weight(std::pair p) - * \brief Linear weight function. - * \ingroup Persistence_representations - * - * @param[in] p point in 2D. - */ -static double linear_weight(std::pair p) {return std::abs(p.second - p.first);} - -/** \fn static double const_weight(std::pair p) - * \brief Constant weight function. - * \ingroup Persistence_representations - * - * @param[in] p point in 2D. - */ -static double const_weight(std::pair p) {return 1;} - -/** \fn static std::function) > arctan_weight(double C, double alpha) - * \brief Returns the arctan weight function with parameters C and alpha. - * \ingroup Persistence_representations - * - * @param[in] C positive constant. - * @param[in] alpha positive power. - */ -static std::function) > arctan_weight(double C, double alpha) {return [=](std::pair p){return C * atan(std::pow(std::abs(p.second - p.first), alpha));};} - -} // namespace Persistence_representations -} // namespace Gudhi - -#endif // WEIGHT_FUNCTIONS_H_ diff --git a/src/Persistence_representations/include/gudhi/common_persistence_representations.h b/src/Persistence_representations/include/gudhi/common_persistence_representations.h index 539eee60..024c99ec 100644 --- a/src/Persistence_representations/include/gudhi/common_persistence_representations.h +++ b/src/Persistence_representations/include/gudhi/common_persistence_representations.h @@ -40,12 +40,23 @@ static constexpr double pi = boost::math::constants::pi(); /** * In this module, we use the name Persistence_diagram for the representation of a diagram in a vector of pairs of two double. */ -using Persistence_diagram = std::vector >; +using Persistence_diagram = std::vector >; /** * In this module, we use the name Weight for the representation of a function taking a pair of two double and returning a double. */ -using Weight = std::function) >; +using Weight = std::function) >; +using Kernel = std::function, std::pair )>; + +Kernel Gaussian_kernel(double sigma){ + return [=](std::pair p, std::pair q){return std::exp( -((p.first-q.first)*(p.first-q.first) + (p.second-q.second)*(p.second-q.second)) / (sigma*sigma) );}; +} + +Kernel polynomial_kernel(double c, double d){ + return [=](std::pair p, std::pair q){return std::pow( p.first*q.first + p.second*q.second + c, d);}; +} + + // double epsi = std::numeric_limits::epsilon(); double epsi = 0.000005; diff --git a/src/cython/cython/kernels.pyx b/src/cython/cython/kernels.pyx deleted file mode 100644 index cb8fc0fd..00000000 --- a/src/cython/cython/kernels.pyx +++ /dev/null @@ -1,128 +0,0 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.utility cimport pair -import os - -"""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): Mathieu Carriere - - Copyright (C) 2018 INRIA - - 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 . -""" - -__author__ = "Mathieu Carriere" -__copyright__ = "Copyright (C) 2018 INRIA" -__license__ = "GPL v3" - -cdef extern from "Kernels_interface.h" namespace "Gudhi::persistence_diagram": - double sw (vector[pair[double, double]], vector[pair[double, double]], double, int) - vector[vector[double]] sw_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) - double pss (vector[pair[double, double]], vector[pair[double, double]], double, int) - vector[vector[double]] pss_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], double, int) - double pwg (vector[pair[double, double]], vector[pair[double, double]], int, string, double, double, double) - vector[vector[double]] pwg_matrix (vector[vector[pair[double, double]]], vector[vector[pair[double, double]]], int, string, double, double, double) - -def sliced_wasserstein(diagram_1, diagram_2, sigma = 1, N = 100): - """ - - :param diagram_1: The first diagram. - :type diagram_1: vector[pair[double, double]] - :param diagram_2: The second diagram. - :type diagram_2: vector[pair[double, double]] - :param sigma: bandwidth of Gaussian - :param N: number of directions - - :returns: the sliced wasserstein kernel. - """ - return sw(diagram_1, diagram_2, sigma, N) - -def sliced_wasserstein_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100): - """ - - :param diagram_1: The first set of diagrams. - :type diagram_1: vector[vector[pair[double, double]]] - :param diagram_2: The second set of diagrams. - :type diagram_2: vector[vector[pair[double, double]]] - :param sigma: bandwidth of Gaussian - :param N: number of directions - - :returns: the sliced wasserstein kernel matrix. - """ - return sw_matrix(diagrams_1, diagrams_2, sigma, N) - -def persistence_weighted_gaussian(diagram_1, diagram_2, N = 100, weight = "arctan", sigma = 1.0, C = 1.0, p = 1.0): - """ - - :param diagram_1: The first diagram. - :type diagram_1: vector[pair[double, double]] - :param diagram_2: The second diagram. - :type diagram_2: vector[pair[double, double]] - :param N: number of Fourier features - :param weight: weight to use for the diagram points - :param sigma: bandwidth of Gaussian - :param C: cost of arctan persistence weight - :param p: power of arctan persistence weight - - :returns: the persistence weighted gaussian kernel. - """ - return pwg(diagram_1, diagram_2, N, weight, sigma, C, p) - -def persistence_weighted_gaussian_matrix(diagrams_1, diagrams_2, N = 100, weight = "arctan", sigma = 1.0, C = 1.0, p = 1.0): - """ - - :param diagram_1: The first set of diagrams. - :type diagram_1: vector[vector[pair[double, double]]] - :param diagram_2: The second set of diagrams. - :type diagram_2: vector[vector[pair[double, double]]] - :param N: number of Fourier features - :param weight: weight to use for the diagram points - :param sigma: bandwidth of Gaussian - :param C: cost of arctan persistence weight - :param p: power of arctan persistence weight - - :returns: the persistence weighted gaussian kernel matrix. - """ - return pwg_matrix(diagrams_1, diagrams_2, N, weight, sigma, C, p) - -def persistence_scale_space(diagram_1, diagram_2, sigma = 1, N = 100): - """ - - :param diagram_1: The first diagram. - :type diagram_1: vector[pair[double, double]] - :param diagram_2: The second diagram. - :type diagram_2: vector[pair[double, double]] - :param sigma: bandwidth of Gaussian - :param N: number of Fourier features - - :returns: the persistence scale space kernel. - """ - return pss(diagram_1, diagram_2, sigma, N) - -def persistence_scale_space_matrix(diagrams_1, diagrams_2, sigma = 1, N = 100): - """ - - :param diagram_1: The first set of diagrams. - :type diagram_1: vector[vector[pair[double, double]]] - :param diagram_2: The second set of diagrams. - :type diagram_2: vector[vector[pair[double, double]]] - :param sigma: bandwidth of Gaussian - :param N: number of Fourier features - - :returns: the persistence scale space kernel matrix. - """ - return pss_matrix(diagrams_1, diagrams_2, sigma, N) diff --git a/src/cython/cython/vectors.pyx b/src/cython/cython/vectors.pyx deleted file mode 100644 index af53f739..00000000 --- a/src/cython/cython/vectors.pyx +++ /dev/null @@ -1,68 +0,0 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.utility cimport pair -import os - -"""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): Mathieu Carriere - - Copyright (C) 2018 INRIA - - 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 . -""" - -__author__ = "Mathieu Carriere" -__copyright__ = "Copyright (C) 2018 INRIA" -__license__ = "GPL v3" - -cdef extern from "Vectors_interface.h" namespace "Gudhi::persistence_diagram": - vector[vector[double]] compute_ls (vector[pair[double, double]], int, double, double, int) - vector[vector[double]] compute_pim (vector[pair[double, double]], double, double, int, double, double, int, string, double, double, double) - -def landscape(diagram, nb_ls = 10, min_x = 0.0, max_x = 1.0, res_x = 100): - """ - - :param diagram: The diagram - :type diagram: vector[pair[double, double]] - :param nb_ls: Number of landscapes - :param min_x: Minimum abscissa - :param max_x: Maximum abscissa - :param res_x: Number of samples - - :returns: the landscape - """ - return compute_ls(diagram, nb_ls, min_x, max_x, res_x) - -def persistence_image(diagram, min_x = 0.0, max_x = 1.0, res_x = 10, min_y = 0.0, max_y = 1.0, res_y = 10, weight = "linear", sigma = 1.0, C = 1.0, p = 1.0): - """ - - :param diagram: The diagram - :type diagram: vector[vector[pair[double, double]]] - :param min_x: Minimum abscissa - :param max_x: Maximum abscissa - :param res_x: Number of abscissa pixels - :param min_x: Minimum ordinate - :param max_x: Maximum ordinate - :param res_x: Number of ordinate pixels - :param weight: Weight to use for the diagram points - :param sigma: bandwidth of Gaussian - :param C: cost of arctan persistence weight - :param p: power of arctan persistence weight - - :returns: the persistence image - """ - return compute_pim(diagram, min_x, max_x, res_x, min_y, max_y, res_y, weight, sigma, C, p) diff --git a/src/cython/include/Kernels_interface.h b/src/cython/include/Kernels_interface.h deleted file mode 100644 index a07d7820..00000000 --- a/src/cython/include/Kernels_interface.h +++ /dev/null @@ -1,130 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 INRIA - * - * 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 INCLUDE_KERNELS_INTERFACE_H_ -#define INCLUDE_KERNELS_INTERFACE_H_ - -#include -#include -#include -#include - -#include -#include -#include // for std::pair - -namespace Gudhi { - -namespace persistence_diagram { - - - // ******************* - // Kernel evaluations. - // ******************* - - double sw(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { - Gudhi::Persistence_representations::Sliced_Wasserstein sw1(diag1, sigma, N); - Gudhi::Persistence_representations::Sliced_Wasserstein sw2(diag2, sigma, N); - return sw1.compute_scalar_product(sw2); - } - - double pwg(const std::vector>& diag1, const std::vector>& diag2, int N, std::string weight, double sigma, double C, double p) { - Gudhi::Persistence_representations::Weight weight_fn; - if(weight.compare("linear") == 0) weight_fn = Gudhi::Persistence_representations::linear_weight; - if(weight.compare("arctan") == 0) weight_fn = Gudhi::Persistence_representations::arctan_weight(C,p); - if(weight.compare("const") == 0) weight_fn = Gudhi::Persistence_representations::const_weight; - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, sigma, N, weight_fn); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, sigma, N, weight_fn); - return pwg1.compute_scalar_product(pwg2); - } - - double pss(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { - std::vector> pd1 = diag1; int numpts = diag1.size(); for(int i = 0; i < numpts; i++) pd1.emplace_back(diag1[i].second,diag1[i].first); - std::vector> pd2 = diag2; numpts = diag2.size(); for(int i = 0; i < numpts; i++) pd2.emplace_back(diag2[i].second,diag2[i].first); - - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(pd1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(pd2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); - - return pwg1.compute_scalar_product (pwg2) / (16*Gudhi::Persistence_representations::pi*sigma); - } - - double pss_sym(const std::vector>& diag1, const std::vector>& diag2, double sigma, int N) { - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg1(diag1, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); - Gudhi::Persistence_representations::Persistence_weighted_gaussian pwg2(diag2, 2*std::sqrt(sigma), N, Gudhi::Persistence_representations::pss_weight); - - return pwg1.compute_scalar_product (pwg2) / (16*Gudhi::Persistence_representations::pi*sigma); - } - - - // **************** - // Kernel matrices. - // **************** - - std::vector > sw_matrix(const std::vector > >& s1, const std::vector > >& s2, double sigma, int N){ - std::vector > matrix; - std::vector ss1; - int num_diag_1 = s1.size(); for(int i = 0; i < num_diag_1; i++){Gudhi::Persistence_representations::Sliced_Wasserstein sw1(s1[i], sigma, N); ss1.push_back(sw1);} - std::vector ss2; - int num_diag_2 = s2.size(); for(int i = 0; i < num_diag_2; i++){Gudhi::Persistence_representations::Sliced_Wasserstein sw2(s2[i], sigma, N); ss2.push_back(sw2);} - for(int i = 0; i < num_diag_1; i++){ - std::cout << 100.0*i/num_diag_1 << " %" << std::endl; - std::vector ps; for(int j = 0; j < num_diag_2; j++) ps.push_back(ss1[i].compute_scalar_product(ss2[j])); matrix.push_back(ps); - } - return matrix; - } - - std::vector > pwg_matrix(const std::vector > >& s1, const std::vector > >& s2, int N, std::string weight, double sigma, double C, double p){ - std::vector > matrix; int num_diag_1 = s1.size(); int num_diag_2 = s2.size(); - for(int i = 0; i < num_diag_1; i++){ - std::cout << 100.0*i/num_diag_1 << " %" << std::endl; - std::vector ps; for(int j = 0; j < num_diag_2; j++) ps.push_back(pwg(s1[i], s2[j], N, weight, sigma, C, p)); matrix.push_back(ps); - } - return matrix; - } - - std::vector > pss_matrix(const std::vector > >& s1, const std::vector > >& s2, double sigma, int N){ - std::vector > > ss1, ss2; std::vector > matrix; int num_diag_1 = s1.size(); int num_diag_2 = s2.size(); - for(int i = 0; i < num_diag_1; i++){ - std::vector> pd1 = s1[i]; int numpts = s1[i].size(); - for(int j = 0; j < numpts; j++) pd1.emplace_back(s1[i][j].second,s1[i][j].first); - ss1.push_back(pd1); - } - - for(int i = 0; i < num_diag_2; i++){ - std::vector> pd2 = s2[i]; int numpts = s2[i].size(); - for(int j = 0; j < numpts; j++) pd2.emplace_back(s2[i][j].second,s2[i][j].first); - ss2.push_back(pd2); - } - - for(int i = 0; i < num_diag_1; i++){ - std::cout << 100.0*i/num_diag_1 << " %" << std::endl; - std::vector ps; for(int j = 0; j < num_diag_2; j++) ps.push_back(pss_sym(ss1[i], ss2[j], sigma, N)); matrix.push_back(ps); - } - return matrix; - } - -} // namespace persistence_diagram - -} // namespace Gudhi - - -#endif // INCLUDE_KERNELS_INTERFACE_H_ diff --git a/src/cython/include/Vectors_interface.h b/src/cython/include/Vectors_interface.h deleted file mode 100644 index 902ccc10..00000000 --- a/src/cython/include/Vectors_interface.h +++ /dev/null @@ -1,59 +0,0 @@ -/* 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): Mathieu Carriere - * - * Copyright (C) 2018 INRIA - * - * 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 INCLUDE_VECTORS_INTERFACE_H_ -#define INCLUDE_VECTORS_INTERFACE_H_ - -#include -#include -#include - -#include -#include -#include // for std::pair - -using Weight = std::function) >; - -namespace Gudhi { - -namespace persistence_diagram { - - std::vector > compute_ls(const std::vector >& diag, int nb_ls, double min_x, double max_x, int res_x) { - Gudhi::Persistence_representations::Persistence_landscape_on_grid_exact L(diag, nb_ls, min_x, max_x, res_x); - return L.vectorize(); - } - - std::vector > compute_pim(const std::vector >& diag, double min_x, double max_x, int res_x, double min_y, double max_y, int res_y, std::string weight, double sigma, double C, double p) { - Weight weight_fn; - if(weight.compare("linear") == 0) weight_fn = Gudhi::Persistence_representations::linear_weight; - if(weight.compare("arctan") == 0) weight_fn = Gudhi::Persistence_representations::arctan_weight(C,p); - if(weight.compare("const") == 0) weight_fn = Gudhi::Persistence_representations::const_weight; - Gudhi::Persistence_representations::Persistence_heat_maps_exact P(diag, min_x, max_x, res_x, min_y, max_y, res_y, weight_fn, sigma); - return P.vectorize(); - } - -} // namespace persistence_diagram - -} // namespace Gudhi - - -#endif // INCLUDE_VECTORS_INTERFACE_H_ -- cgit v1.2.3 From 0c372ac3217ef31607c25266ff4394b5fa1ca2a8 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Tue, 3 Jul 2018 05:52:22 +0000 Subject: corrected test units git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3662 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 5b37dde84d00538ff15b7e638ba1f2d6800573c0 --- .../include/gudhi/common_persistence_representations.h | 2 +- src/Persistence_representations/test/kernels.cpp | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Persistence_representations/include/gudhi/common_persistence_representations.h b/src/Persistence_representations/include/gudhi/common_persistence_representations.h index 024c99ec..66ed3bf8 100644 --- a/src/Persistence_representations/include/gudhi/common_persistence_representations.h +++ b/src/Persistence_representations/include/gudhi/common_persistence_representations.h @@ -49,7 +49,7 @@ using Weight = std::function) >; using Kernel = std::function, std::pair )>; Kernel Gaussian_kernel(double sigma){ - return [=](std::pair p, std::pair q){return std::exp( -((p.first-q.first)*(p.first-q.first) + (p.second-q.second)*(p.second-q.second)) / (sigma*sigma) );}; + return [=](std::pair p, std::pair q){return (1.0 / (std::sqrt(2*pi)*sigma)) * std::exp( -((p.first-q.first)*(p.first-q.first) + (p.second-q.second)*(p.second-q.second)) / (2*sigma*sigma) );}; } Kernel polynomial_kernel(double c, double d){ diff --git a/src/Persistence_representations/test/kernels.cpp b/src/Persistence_representations/test/kernels.cpp index 9db19123..c95e8086 100644 --- a/src/Persistence_representations/test/kernels.cpp +++ b/src/Persistence_representations/test/kernels.cpp @@ -29,21 +29,22 @@ #include #include #include // std::max +#include #include -#include -#include #include #include #include +using constant_scaling_function = Gudhi::Persistence_representations::constant_scaling_function; using SW = Gudhi::Persistence_representations::Sliced_Wasserstein; -using PWG = Gudhi::Persistence_representations::Persistence_weighted_gaussian; +using PWG = Gudhi::Persistence_representations::Persistence_heat_maps; +using Persistence_diagram = std::vector >; BOOST_AUTO_TEST_CASE(check_PWG) { Persistence_diagram v1, v2; v1.emplace_back(0,1); v2.emplace_back(0,2); - PWG pwg1(v1, 1.0, 1000, Gudhi::Persistence_representations::arctan_weight(1,1)); PWG pwgex1(v1, 1.0, -1, Gudhi::Persistence_representations::arctan_weight(1,1)); - PWG pwg2(v2, 1.0, 1000, Gudhi::Persistence_representations::arctan_weight(1,1)); PWG pwgex2(v2, 1.0, -1, Gudhi::Persistence_representations::arctan_weight(1,1)); - BOOST_CHECK(std::abs(pwg1.compute_scalar_product(pwg2) - pwgex1.compute_scalar_product(pwgex2)) <= 1e-1); + PWG pwg1(v1, Gudhi::Persistence_representations::Gaussian_kernel(1.0)); + PWG pwg2(v2, Gudhi::Persistence_representations::Gaussian_kernel(1.0)); + BOOST_CHECK(std::abs(pwg1.compute_scalar_product(pwg2) - std::exp(-0.5)/(std::sqrt(2*Gudhi::Persistence_representations::pi))) <= 1e-3); } BOOST_AUTO_TEST_CASE(check_SW) { -- cgit v1.2.3 From 6d00273077db54d609262e79702cbd5a94491105 Mon Sep 17 00:00:00 2001 From: mcarrier Date: Thu, 23 Aug 2018 21:11:47 +0000 Subject: git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3827 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: a6a297a7f14703e55954706328072acdd447484c --- .../include/gudhi/Persistence_heat_maps.h | 2 -- .../include/gudhi/Sliced_Wasserstein.h | 42 ++++++---------------- src/cmake/modules/GUDHI_modules.cmake | 2 +- 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/src/Persistence_representations/include/gudhi/Persistence_heat_maps.h b/src/Persistence_representations/include/gudhi/Persistence_heat_maps.h index 12188526..43f10b8c 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_heat_maps.h +++ b/src/Persistence_representations/include/gudhi/Persistence_heat_maps.h @@ -1002,8 +1002,6 @@ double Persistence_heat_maps::compute_scalar_product(const } else{ - GUDHI_CHECK(this->approx != second.approx || this->f != second.f, std::invalid_argument("Error: different values for representations")); - int num_pts1 = this->d.size(); int num_pts2 = second.d.size(); double kernel_val = 0; for(int i = 0; i < num_pts1; i++) for(int j = 0; j < num_pts2; j++) diff --git a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h index a3c0dc2f..6f67f7bc 100644 --- a/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h +++ b/src/Persistence_representations/include/gudhi/Sliced_Wasserstein.h @@ -97,23 +97,7 @@ class Sliced_Wasserstein { // Compute the angle formed by two points of a PD double compute_angle(const Persistence_diagram & diag, int i, int j) const { - std::pair vect; double x1,y1, x2,y2; - x1 = diag[i].first; y1 = diag[i].second; - x2 = diag[j].first; y2 = diag[j].second; - if (y1 - y2 > 0){ - vect.first = y1 - y2; - vect.second = x2 - x1;} - else{ - if(y1 - y2 < 0){ - vect.first = y2 - y1; - vect.second = x1 - x2; - } - else{ - vect.first = 0; - vect.second = abs(x1 - x2);} - } - double norm = std::sqrt(vect.first*vect.first + vect.second*vect.second); - return asin(vect.second/norm); + if(diag[i].second == diag[j].second) return pi/2; else return atan((diag[j].first-diag[i].first)/(diag[i].second-diag[j].second)); } // Compute the integral of |cos()| between alpha and beta, valid only if alpha is in [-pi,pi] and beta-alpha is in [0,pi] @@ -145,10 +129,7 @@ class Sliced_Wasserstein { double compute_int(double theta1, double theta2, int p, int q, const Persistence_diagram & diag1, const Persistence_diagram & diag2) const { double norm = std::sqrt( (diag1[p].first-diag2[q].first)*(diag1[p].first-diag2[q].first) + (diag1[p].second-diag2[q].second)*(diag1[p].second-diag2[q].second) ); double angle1; - if (diag1[p].first > diag2[q].first) - angle1 = theta1 - asin( (diag1[p].second-diag2[q].second)/norm ); - else - angle1 = theta1 - asin( (diag2[q].second-diag1[p].second)/norm ); + if (diag1[p].first == diag2[q].first) angle1 = theta1 - pi/2; else angle1 = theta1 - atan((diag1[p].second-diag2[q].second)/(diag1[p].first-diag2[q].first)); double angle2 = angle1 + theta2 - theta1; double integral = compute_int_cos(angle1,angle2); return norm*integral; @@ -164,24 +145,23 @@ class Sliced_Wasserstein { if(this->approx == -1){ // Add projections onto diagonal. - int n1, n2; n1 = diagram1.size(); n2 = diagram2.size(); double max_ordinate = std::numeric_limits::lowest(); + int n1, n2; n1 = diagram1.size(); n2 = diagram2.size(); double min_ordinate = std::numeric_limits::max(); double min_abscissa = std::numeric_limits::max(); for (int i = 0; i < n2; i++){ - max_ordinate = std::max(max_ordinate, diagram2[i].second); + min_ordinate = std::min(min_ordinate, diagram2[i].second); min_abscissa = std::min(min_abscissa, diagram2[i].first); diagram1.emplace_back( (diagram2[i].first+diagram2[i].second)/2, (diagram2[i].first+diagram2[i].second)/2 ); } for (int i = 0; i < n1; i++){ - max_ordinate = std::max(max_ordinate, diagram1[i].second); + min_ordinate = std::min(min_ordinate, diagram1[i].second); min_abscissa = std::min(min_abscissa, diagram1[i].first); diagram2.emplace_back( (diagram1[i].first+diagram1[i].second)/2, (diagram1[i].first+diagram1[i].second)/2 ); } int num_pts_dgm = diagram1.size(); // Slightly perturb the points so that the PDs are in generic positions. - int mag = 0; while(max_ordinate > 10){mag++; max_ordinate/=10;} - double thresh = pow(10,-5+mag); + double thresh_y = pow(10,log10(min_ordinate)-5); double thresh_x = pow(10,log10(min_abscissa)-5); srand(time(NULL)); for (int i = 0; i < num_pts_dgm; i++){ - diagram1[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); diagram1[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); - diagram2[i].first += thresh*(1.0-2.0*rand()/RAND_MAX); diagram2[i].second += thresh*(1.0-2.0*rand()/RAND_MAX); + diagram1[i].first += thresh_x*(1.0-2.0*rand()/RAND_MAX); diagram1[i].second += thresh_y*(1.0-2.0*rand()/RAND_MAX); + diagram2[i].first += thresh_x*(1.0-2.0*rand()/RAND_MAX); diagram2[i].second += thresh_y*(1.0-2.0*rand()/RAND_MAX); } // Compute all angles in both PDs. @@ -201,8 +181,8 @@ class Sliced_Wasserstein { // Initialize orders of the points of both PDs (given by ordinates when theta = -pi/2). std::vector orderp1, orderp2; for (int i = 0; i < num_pts_dgm; i++){ orderp1.push_back(i); orderp2.push_back(i); } - std::sort( orderp1.begin(), orderp1.end(), [=](int i, int j){ if(diagram1[i].second != diagram1[j].second) return (diagram1[i].second < diagram1[j].second); else return (diagram1[i].first > diagram1[j].first); } ); - std::sort( orderp2.begin(), orderp2.end(), [=](int i, int j){ if(diagram2[i].second != diagram2[j].second) return (diagram2[i].second < diagram2[j].second); else return (diagram2[i].first > diagram2[j].first); } ); + std::sort( orderp1.begin(), orderp1.end(), [&](int i, int j){ if(diagram1[i].second != diagram1[j].second) return (diagram1[i].second < diagram1[j].second); else return (diagram1[i].first > diagram1[j].first); } ); + std::sort( orderp2.begin(), orderp2.end(), [&](int i, int j){ if(diagram2[i].second != diagram2[j].second) return (diagram2[i].second < diagram2[j].second); else return (diagram2[i].first > diagram2[j].first); } ); // Find the inverses of the orders. std::vector order1(num_pts_dgm); std::vector order2(num_pts_dgm); @@ -274,6 +254,7 @@ class Sliced_Wasserstein { public: /** \brief Sliced Wasserstein kernel constructor. + * \implements Topological_data_with_distances, Real_valued_topological_data, Topological_data_with_scalar_product * \ingroup Sliced_Wasserstein * * @param[in] _diagram persistence diagram. @@ -282,7 +263,6 @@ class Sliced_Wasserstein { * points on all directions are stored in memory to reduce computation time. * */ - // This class implements the following concepts: Topological_data_with_distances, Real_valued_topological_data, Topological_data_with_scalar_product Sliced_Wasserstein(const Persistence_diagram & _diagram, double _sigma = 1.0, int _approx = 10):diagram(_diagram), approx(_approx), sigma(_sigma) {build_rep();} /** \brief Evaluation of the kernel on a pair of diagrams. diff --git a/src/cmake/modules/GUDHI_modules.cmake b/src/cmake/modules/GUDHI_modules.cmake index 276fb2cc..f95d0c34 100644 --- a/src/cmake/modules/GUDHI_modules.cmake +++ b/src/cmake/modules/GUDHI_modules.cmake @@ -17,7 +17,7 @@ function(add_gudhi_module file_path) endfunction(add_gudhi_module) option(WITH_GUDHI_BENCHMARK "Activate/desactivate benchmark compilation" OFF) -option(WITH_GUDHI_EXAMPLE "Activate/desactivate examples compilation and installation" ON) +option(WITH_GUDHI_EXAMPLE "Activate/desactivate examples compilation and installation" OFF) option(WITH_GUDHI_PYTHON "Activate/desactivate python module compilation and installation" ON) option(WITH_GUDHI_TEST "Activate/desactivate examples compilation and installation" ON) option(WITH_GUDHI_UTILITIES "Activate/desactivate utilities compilation and installation" ON) -- cgit v1.2.3 From cc1a09fa5b00b7bea1d3f7bac0cce3c401d23dce Mon Sep 17 00:00:00 2001 From: mcarrier Date: Thu, 23 Aug 2018 21:13:02 +0000 Subject: rm Doxyfile git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/kernels@3828 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 501fca20271b60930f8078d29413a192c87f1962 --- src/Doxyfile | 2316 ---------------------------------------------------------- 1 file changed, 2316 deletions(-) delete mode 100644 src/Doxyfile diff --git a/src/Doxyfile b/src/Doxyfile deleted file mode 100644 index da753c04..00000000 --- a/src/Doxyfile +++ /dev/null @@ -1,2316 +0,0 @@ -# Doxyfile 1.8.6 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = "GUDHI" - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = "2.2.0" - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "C++ library for Topological Data Analysis (TDA) and Higher Dimensional Geometry Understanding." - -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = "doc/" - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = YES - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = include concept - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. -# -# Note For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = NO - -# This flag is only useful for Objective-C code. When set to YES local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = YES - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = YES - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = NO - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. -# The default value is: YES. - -GENERATE_TODOLIST = NO - -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. -# The default value is: YES. - -GENERATE_TESTLIST = NO - -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = NO - -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= NO - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. - -CITE_BIB_FILES = biblio/bibliography.bib \ - biblio/how_to_cite_cgal.bib \ - biblio/how_to_cite_gudhi.bib - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. -# Note: If this tag is empty the current directory is searched. - -INPUT = - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = data/ \ - example/ \ - GudhUI/ \ - cmake/ \ - src/cython/ \ - include/gudhi_patches/ - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = */utilities/*/*.md - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = biblio/ \ - example/ \ - utilities/ - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = doc/Skeleton_blocker/ \ - doc/Alpha_complex/ \ - doc/common/ \ - doc/Cech_complex/ \ - doc/Contraction/ \ - doc/Simplex_tree/ \ - doc/Persistent_cohomology/ \ - doc/Witness_complex/ \ - doc/Bitmap_cubical_complex/ \ - doc/Rips_complex/ \ - doc/Subsampling/ \ - doc/Spatial_searching/ \ - doc/Tangential_complex/ \ - doc/Bottleneck_distance/ \ - doc/Nerve_GIC/ \ - doc/Persistence_representations/ \ - doc/Kernels/ - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = doc/common/header.html - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = doc/common/footer.html - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = doc/common/stylesheet.css - -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = YES - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = YES - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = YES - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = ../common - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /