From ed66516c2001022e467da0c8b4f229015813a1dc Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:51:31 -0400 Subject: added sklearn_tda preprocessing functions --- src/cython/sktda/preprocessing.py | 274 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 src/cython/sktda/preprocessing.py diff --git a/src/cython/sktda/preprocessing.py b/src/cython/sktda/preprocessing.py new file mode 100644 index 00000000..2f4a5fe5 --- /dev/null +++ b/src/cython/sktda/preprocessing.py @@ -0,0 +1,274 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import StandardScaler + +############################################# +# Preprocessing ############################# +############################################# + +class BirthPersistenceTransform(BaseEstimator, TransformerMixin): + """ + This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. It is a particular scaler for persistence diagram that can be given as input for the DiagramPreprocessor class. + """ + def __init__(self): + """ + Constructor for BirthPersistenceTransform class. + """ + return None + + def fit(self, X, y=None): + """ + Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Apply the BirthPersistenceTransform function on the persistence diagrams. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 numpy arrays): transformed persistence diagrams. + """ + Xfit = np.matmul(X, np.array([[1., -1.],[0., 1.]])) + return Xfit + + +class DiagramPreprocessor(BaseEstimator, TransformerMixin): + """ + This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. + """ + def __init__(self, use=False, scalers=[]): + """ + Constructor for the DiagramPreprocessor class. + + Attributes: + use (bool): whether to use the class or not (default False). + scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). + """ + self.scalers = scalers + self.use = use + + def fit(self, X, y=None): + """ + Fit the DiagramPreprocessor class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if self.use: + if len(X) == 1: + P = X[0] + else: + P = np.concatenate(X,0) + for (indices, scaler) in self.scalers: + scaler.fit(P[:,indices]) + return self + + def transform(self, X): + """ + Apply the DiagramPreprocessor function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): transformed persistence diagrams. + """ + Xfit = [np.copy(d) for d in X] + if self.use: + for i in range(len(Xfit)): + if Xfit[i].shape[0] > 0: + for (indices, scaler) in self.scalers: + Xfit[i][:,indices] = scaler.transform(Xfit[i][:,indices]) + return Xfit + +class Padding(BaseEstimator, TransformerMixin): + """ + This is a class for padding a list of persistence diagrams with dummy points, so that all persistence diagrams end up with the same number of points. + """ + def __init__(self, use=False): + """ + Constructor for the Padding class. + + Attributes: + use (bool): whether to use the class or not (default False). + """ + self.use = use + + def fit(self, X, y=None): + """ + Fit the Padding class on a list of persistence diagrams (this function actually does nothing but is useful when Padding is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Add dummy points to each persistence diagram so that they all have the same cardinality. All points are given an additional coordinate indicating if the point was added after padding (0) or already present before (1). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 3 or n x 2 numpy arrays): padded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + max_card = max([len(diag) for diag in X]) + for diag in X: + [num_pts, dim] = diag.shape + diag_pad = np.zeros([max_card, dim+1]) + diag_pad[:num_pts,:dim] = diag + diag_pad[:num_pts, dim] = np.ones(num_pts) + Xfit.append(diag_pad) + else: + Xfit = X + return Xfit + +class ProminentPoints(BaseEstimator, TransformerMixin): + """ + This is a class for removing points that are close or far from the diagonal in persistence diagrams. + """ + def __init__(self, use=False, num_pts=10, threshold=-1, location="upper", point_type="finite"): + """ + Constructor for the ProminentPoints class. + + Attributes: + use (bool): whether to use the class or not (default False). + location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. + num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. + threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. + point_type (string): either "finite" if persistence diagrams are n x 2 numpy arrays, or "essential" if persistence diagrams are n x 1 numpy arrays (default "finite"). If "finite", points are ordered and thresholded by distance-to-diagonal. If "essential", points are ordered and thresholded by first coordinate. + """ + self.num_pts = num_pts + self.threshold = threshold + self.use = use + self.location = location + self.point_type = point_type + + def fit(self, X, y=None): + """ + Fit the ProminentPoints class on a list of persistence diagrams (this function actually does nothing but is useful when ProminentPoints is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. If point_type == "essential", do the same with first coordinate instead of distance-to-diagonal. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): thresholded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + for i in range(num_diag): + diag = X[i] + if self.point_type == "finite": + if diag.shape[0] > 0: + pers = np.abs(np.matmul(diag[:,:2], [-1., 1.])) + idx_thresh = pers >= self.threshold + thresh_diag, thresh_pers = diag[idx_thresh.flatten()], pers[idx_thresh.flatten()] + sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) + if self.location == "upper": + new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh.flatten()] ], axis=0) + else: + new_diag = diag + + else: + if diag.shape[0] > 0: + birth = diag[:,:1] + idx_thresh = birth >= self.threshold + thresh_diag, thresh_birth = diag[idx_thresh.flatten()], birth[idx_thresh.flatten()] + if self.location == "upper": + new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh.flatten()] ], axis=0) + else: + new_diag = diag + + Xfit.append(new_diag) + else: + Xfit = X + return Xfit + +class DiagramSelector(BaseEstimator, TransformerMixin): + """ + This is a class for extracting finite or essential points in persistence diagrams. + """ + def __init__(self, use=False, limit=np.inf, point_type="finite"): + """ + Constructor for the DiagramSelector class. + + Attributes: + use (bool): whether to use the class or not (default False). + limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). + point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. + """ + self.use, self.limit, self.point_type = use, limit, point_type + + def fit(self, X, y=None): + """ + Fit the DiagramSelector class on a list of persistence diagrams (this function actually does nothing but is useful when DiagramSelector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Extract and return the finite or essential points of each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): extracted persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + if self.point_type == "finite": + for i in range(num_diag): + diag = X[i] + if diag.shape[0] != 0: + idx_fin = diag[:,1] != self.limit + Xfit.append(diag[idx_fin,:]) + else: + Xfit.append(diag) + if self.point_type == "essential": + for i in range(num_diag): + diag = X[i] + if diag.shape[0] != 0: + idx_ess = diag[:,1] == self.limit + Xfit.append(np.delete(diag,1,1)[idx_ess,:]) + else: + Xfit.append(np.delete(diag,1,1)) + else: + Xfit = X + return Xfit -- cgit v1.2.3 From de65ea32902e31bc1f951d32eeb035ae95c624db Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:51:45 -0400 Subject: added sklearn_tda vectorizations --- src/cython/sktda/vector_methods.py | 404 +++++++++++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 src/cython/sktda/vector_methods.py diff --git a/src/cython/sktda/vector_methods.py b/src/cython/sktda/vector_methods.py new file mode 100644 index 00000000..4dd147e7 --- /dev/null +++ b/src/cython/sktda/vector_methods.py @@ -0,0 +1,404 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import MinMaxScaler +from sklearn.neighbors import DistanceMetric + +from preprocessing import DiagramPreprocessor + +############################################# +# Finite Vectorization methods ############## +############################################# + +class PersistenceImage(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence images from a list of persistence diagrams. A persistence image is a 2D function computed from a persistence diagram by convolving the diagram points with a weighted Gaussian kernel. The plane is then discretized into an image with pixels, which is flattened and returned as a vector. See http://jmlr.org/papers/v18/16-337.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, resolution=[20,20], im_range=[np.nan, np.nan, np.nan, np.nan]): + """ + Constructor for the PersistenceImage class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel (default 1.). + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). + im_range ([double,double,double,double]): minimum and maximum of each axis of the persistence image, of the form [x_min, x_max, y_min, y_max] (default [numpy.nan, numpy.nan, numpy.nan, numpyp.nan]). If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.bandwidth, self.weight = bandwidth, weight + self.resolution, self.im_range = resolution, im_range + + def fit(self, X, y=None): + """ + Fit the PersistenceImage class on a list of persistence diagrams: if any of the values in **im_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.im_range)).any(): + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) + return self + + def transform(self, X): + """ + Compute the persistence image for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. + """ + num_diag, Xfit = len(X), [] + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + w = np.ones(num_pts_in_diag) + for j in range(num_pts_in_diag): + w[j] = self.weight(diagram[j,:]) + + x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) + Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) + image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(self.bandwidth*np.sqrt(2*np.pi)), 1) + + Xfit.append(image.flatten()[np.newaxis,:]) + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Landscape(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. + """ + def __init__(self, num_landscapes=5, resolution=100, ls_range=[np.nan, np.nan]): + """ + Constructor for the Landscape class. + + Attributes: + num_landscapes (int): number of piecewise-linear functions to output (default 5). + resolution (int): number of sample for all piecewise-linear functions (default 100). + ls_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.num_landscapes, self.resolution, self.ls_range = num_landscapes, resolution, ls_range + + def fit(self, X, y=None): + """ + Fit the Landscape class on a list of persistence diagrams: if any of the values in **ls_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.ls_range)).any(): + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + self.ls_range = np.where(np.isnan(np.array(self.ls_range)), np.array([mx, My]), np.array(self.ls_range)) + return self + + def transform(self, X): + """ + Compute the persistence landscape for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**)): output persistence landscapes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.ls_range[0], self.ls_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + ls = np.zeros([self.num_landscapes, self.resolution]) + + events = [] + for j in range(self.resolution): + events.append([]) + + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:] + min_idx = np.minimum(np.maximum(np.ceil((px - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + landscape_value = self.ls_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + events[k].append(landscape_value) + landscape_value += step_x + + landscape_value = py - self.ls_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + events[k].append(landscape_value) + landscape_value -= step_x + + for j in range(self.resolution): + events[j].sort(reverse=True) + for k in range( min(self.num_landscapes, len(events[j])) ): + ls[k,j] = events[j][k] + + Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Silhouette(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. + """ + def __init__(self, weight=lambda x: 1, resolution=100, sh_range=[np.nan, np.nan]): + """ + Constructor for the Silhouette class. + + Attributes: + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. + resolution (int): number of samples for the weighted average (default 100). + sh_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.weight, self.resolution, self.sh_range = weight, resolution, sh_range + + def fit(self, X, y=None): + """ + Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sh_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sh_range)).any(): + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + self.sh_range = np.where(np.isnan(np.array(self.sh_range)), np.array([mx, My]), np.array(self.sh_range)) + return self + + def transform(self, X): + """ + Compute the persistence silhouette for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sh_range[0], self.sh_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + sh, weights = np.zeros(self.resolution), np.zeros(num_pts_in_diag) + for j in range(num_pts_in_diag): + weights[j] = self.weight(diagram[j,:]) + total_weight = np.sum(weights) + + for j in range(num_pts_in_diag): + + [px,py] = diagram[j,:] + weight = weights[j] / total_weight + min_idx = np.minimum(np.maximum(np.ceil((px - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + silhouette_value = self.sh_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + sh[k] += weight * silhouette_value + silhouette_value += step_x + + silhouette_value = py - self.sh_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + sh[k] += weight * silhouette_value + silhouette_value -= step_x + + Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class BettiCurve(BaseEstimator, TransformerMixin): + """ + This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. + """ + def __init__(self, resolution=100, bc_range=[np.nan, np.nan]): + """ + Constructor for the BettiCurve class. + + Attributes: + resolution (int): number of sample for the piecewise-constant function (default 100). + bc_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.resolution, self.bc_range = resolution, bc_range + + def fit(self, X, y=None): + """ + Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **bc_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.bc_range)).any(): + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + self.bc_range = np.where(np.isnan(np.array(self.bc_range)), np.array([mx, My]), np.array(self.bc_range)) + return self + + def transform(self, X): + """ + Compute the Betti curve for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.bc_range[0], self.bc_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + bc = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:] + min_idx = np.minimum(np.maximum(np.ceil((px - self.bc_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.bc_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + bc[k] += 1 + + Xfit.append(np.reshape(bc,[1,-1])) + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class TopologicalVector(BaseEstimator, TransformerMixin): + """ + This is a class for computing topological vectors from a list of persistence diagrams. The topological vector associated to a persistence diagram is the sorted vector of a slight modification of the pairwise distances between the persistence diagram points. See https://diglib.eg.org/handle/10.1111/cgf12692 for more details. + """ + def __init__(self, threshold=10): + """ + Constructor for the TopologicalVector class. + + Attributes: + threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. + """ + self.threshold = threshold + + def fit(self, X, y=None): + """ + Fit the TopologicalVector class on a list of persistence diagrams (this function actually does nothing but is useful when TopologicalVector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the topological vector for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + num_diag = len(X) + Xfit = np.zeros([num_diag, thresh]) + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + pers = 0.5 * np.matmul(diagram, np.array([[-1.0],[1.0]])) + min_pers = np.minimum(pers,np.transpose(pers)) + distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) + vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) + dim = min(len(vect), thresh) + Xfit[i, :dim] = vect[:dim] + + return Xfit + +class ComplexPolynomial(BaseEstimator, TransformerMixin): + """ + This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. + """ + def __init__(self, F="R", threshold=10): + """ + Constructor for the ComplexPolynomial class. + + Attributes: + F (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). + threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. + """ + self.threshold, self.F = threshold, F + + def fit(self, X, y=None): + """ + Fit the ComplexPolynomial class on a list of persistence diagrams (this function actually does nothing but is useful when ComplexPolynomial is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the complex vector of coefficients for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) + for d in range(len(X)): + D, N = X[d], X[d].shape[0] + if self.F == "R": + roots = D[:,0] + 1j * D[:,1] + elif self.F == "S": + alpha = np.linalg.norm(D, axis=1) + alpha = np.where(alpha==0, np.ones(N), alpha) + roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) + elif self.F == "T": + alpha = np.linalg.norm(D, axis=1) + roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) + coeff = [0] * (N+1) + coeff[N] = 1 + for i in range(1, N+1): + for j in range(N-i-1, N): + coeff[j] += ((-1) * roots[i-1] * coeff[j+1]) + coeff = np.array(coeff[::-1])[1:] + Xfit[d, :min(thresh, coeff.shape[0])] = coeff[:min(thresh, coeff.shape[0])] + return Xfit -- cgit v1.2.3 From 6b5bc4da619d96129d31764f477afeda49670ccf Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:51:59 -0400 Subject: added sklearn_tda kernels --- src/cython/sktda/kernel_methods.py | 202 +++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 src/cython/sktda/kernel_methods.py diff --git a/src/cython/sktda/kernel_methods.py b/src/cython/sktda/kernel_methods.py new file mode 100644 index 00000000..57bfafd7 --- /dev/null +++ b/src/cython/sktda/kernel_methods.py @@ -0,0 +1,202 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +from metrics import SlicedWassersteinDistance, PersistenceFisherDistance + +############################################# +# Kernel methods ############################ +############################################# + +class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein kernel matrix from a list of persistence diagrams. The sliced Wasserstein kernel is computed by exponentiating the corresponding sliced Wasserstein distance with a Gaussian kernel. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10, bandwidth=1.0): + """ + Constructor for the SlicedWassersteinDistance class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). + num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + """ + self.bandwidth = bandwidth + self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinKernel class on a list of persistence diagrams: an instance of the SlicedWassersteinDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.sw_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein kernel values. + """ + return np.exp(-self.sw_.transform(X)/self.bandwidth) + +class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence weighted Gaussian kernel matrix from a list of persistence diagrams. The persistence weighted Gaussian kernel is computed by convolving the persistence diagram points with weighted Gaussian kernels. See http://proceedings.mlr.press/v48/kusano16.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, kernel_approx=None): + """ + Constructor for the PersistenceWeightedGaussianKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.weight = bandwidth, weight + self.kernel_approx = kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceWeightedGaussianKernel class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + self.ws_ = [ np.array([self.weight(self.diagrams_[i][j,:]) for j in range(self.diagrams_[i].shape[0])]) for i in range(len(self.diagrams_)) ] + if self.kernel_approx is not None: + self.approx_ = np.concatenate([np.sum(np.multiply(self.ws_[i][:,np.newaxis], self.kernel_approx.transform(self.diagrams_[i])), axis=0)[np.newaxis,:] for i in range(len(self.diagrams_))]) + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence weighted Gaussian kernel values. + """ + Xp = list(X) + Xfit = np.zeros((len(Xp), len(self.diagrams_))) + if len(self.diagrams_) == len(Xp) and np.all([np.array_equal(self.diagrams_[i], Xp[i]) for i in range(len(Xp))]): + if self.kernel_approx is not None: + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(self.approx_, self.approx_.T) + else: + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + Xfit[j,i] = X[i,j] + else: + ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] + if self.kernel_approx is not None: + approx = np.concatenate([np.sum(np.multiply(ws[i][:,np.newaxis], self.kernel_approx.transform(Xp[i])), axis=0)[np.newaxis,:] for i in range(len(Xp))]) + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(approx, self.approx_.T) + else: + for i in range(len(Xp)): + for j in range(len(self.diagrams_)): + W = np.matmul(ws[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(Xp[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + + return Xfit + +class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceScaleSpaceKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.pwg_ = PersistenceWeightedGaussianKernel(bandwidth=bandwidth, weight=lambda x: 1 if x[1] >= x[0] else -1, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceScaleSpaceKernel class on a list of persistence diagrams: symmetric to the diagonal of all points are computed and an instance of the PersistenceWeightedGaussianKernel class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + for i in range(len(self.diagrams_)): + op_D = np.matmul(self.diagrams_[i], np.array([[0.,1.], [1.,0.]])) + self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) + self.pwg_.fit(X) + return self + + def transform(self, X): + """ + Compute all persistence scale space kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence scale space kernel values. + """ + Xp = list(X) + for i in range(len(Xp)): + op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) + Xp[i] = np.concatenate([Xp[i], op_X], axis=0) + return self.pwg_.transform(Xp) + +class PersistenceFisherKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher kernel matrix from a list of persistence diagrams. The persistence Fisher kernel is computed by exponentiating the corresponding persistence Fisher distance with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth_fisher=1., bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). + bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth = bandwidth + self.pf_ = PersistenceFisherDistance(bandwidth=bandwidth_fisher, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherKernel class on a list of persistence diagrams: an instance of the PersistenceFisherDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.pf_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all persistence Fisher kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher kernel values. + """ + return np.exp(-self.pf_.transform(X)/self.bandwidth) + -- cgit v1.2.3 From a27340146f7b82d7e64c6e9236141354232b56e1 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:52:11 -0400 Subject: added sklearn_tda metrics --- src/cython/sktda/metrics.py | 226 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 src/cython/sktda/metrics.py diff --git a/src/cython/sktda/metrics.py b/src/cython/sktda/metrics.py new file mode 100644 index 00000000..05141e8b --- /dev/null +++ b/src/cython/sktda/metrics.py @@ -0,0 +1,226 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import sys +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +try: + from gudhi import bottleneck_distance + USE_GUDHI = True +except ImportError: + USE_GUDHI = False + print("Gudhi not found: BottleneckDistance will return null matrix, and exact SlicedWassersteinDistance not available") + +############################################# +# Metrics ################################### +############################################# + +class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein distance matrix from a list of persistence diagrams. The Sliced Wasserstein distance is computed by projecting the persistence diagrams onto lines, comparing the projections with the 1-norm, and finally integrating over all possible lines. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10): + """ + Constructor for the SlicedWassersteinDistance class. + + Attributes: + num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the distance computation (default 10). + """ + self.num_directions = num_directions + thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] + self.lines_ = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=0) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in a numpy array called **diagrams**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + self.approx_ = [np.matmul(X[i], self.lines_) for i in range(len(X))] + diag_proj = (1./2) * np.ones((2,2)) + self.approx_diag_ = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein distances. + """ + Xfit = np.zeros((len(X), len(self.approx_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.approx_)): + for j in range(i+1, len(self.approx_)): + A = np.sort(np.concatenate([self.approx_[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], self.approx_diag_[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + Xfit[j,i] = Xfit[i,j] + else: + diag_proj = (1./2) * np.ones((2,2)) + approx = [np.matmul(X[i], self.lines_) for i in range(len(X))] + approx_diag = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + for i in range(len(approx)): + for j in range(len(self.approx_)): + A = np.sort(np.concatenate([approx[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], approx_diag[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + + return Xfit + +class BottleneckDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. + """ + def __init__(self, epsilon=1e-3): + """ + Constructor for the BottleneckDistance class. + + Attributes: + epsilon (double): approximation quality (default 1e-4). + """ + self.epsilon = epsilon + + def fit(self, X, y=None): + """ + Fit the BottleneckDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + return self + + def transform(self, X): + """ + Compute all bottleneck distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise bottleneck distances. + """ + num_diag1 = len(X) + + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + matrix = np.zeros((num_diag1, num_diag1)) + + if USE_GUDHI: + for i in range(num_diag1): + #sys.stdout.write( str(i*1.0 / num_diag1) + "\r") + for j in range(i+1, num_diag1): + matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) + matrix[j,i] = matrix[i,j] + else: + print("Gudhi required---returning null matrix") + + else: + num_diag2 = len(self.diagrams_) + matrix = np.zeros((num_diag1, num_diag2)) + + if USE_GUDHI: + for i in range(num_diag1): + #sys.stdout.write( str(i*1.0 / num_diag1) + "\r") + for j in range(num_diag2): + matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) + else: + print("Gudhi required---returning null matrix") + + Xfit = matrix + + return Xfit + +class PersistenceFisherDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherDistance class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.kernel_approx = bandwidth, kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + projection = (1./2) * np.ones((2,2)) + self.diagonal_projections_ = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + self.approx_ = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + self.approx_diagonal_ = [self.kernel_approx.transform(self.diagonal_projections_[i]) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all persistence Fisher distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher distances. + """ + Xfit = np.zeros((len(X), len(self.diagrams_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) + vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) + vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + Xfit[j,i] = Xfit[i,j] + else: + Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + Xfit[j,i] = Xfit[i,j] + else: + projection = (1./2) * np.ones((2,2)) + diagonal_projections = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + approx = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + approx_diagonal = [self.kernel_approx.transform(diagonal_projections[i]) for i in range(len(X))] + for i in range(len(X)): + for j in range(len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) + vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) + vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + else: + Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + return Xfit -- cgit v1.2.3 From b0218cdb108b444b0403cfd3a782158a6772fc30 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:52:25 -0400 Subject: added sklearn_tda clustering --- src/cython/sktda/clustering.py | 269 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 src/cython/sktda/clustering.py diff --git a/src/cython/sktda/clustering.py b/src/cython/sktda/clustering.py new file mode 100644 index 00000000..d3dd531b --- /dev/null +++ b/src/cython/sktda/clustering.py @@ -0,0 +1,269 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +import itertools + +from metrics import BottleneckDistance +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.cluster import DBSCAN, AgglomerativeClustering +from sklearn.metrics import pairwise_distances +from sklearn.neighbors import radius_neighbors_graph, kneighbors_graph +from scipy.spatial.distance import directed_hausdorff +from scipy.sparse import csgraph + +try: + import gudhi as gd + USE_GUDHI = True + +except ImportError: + USE_GUDHI = False + print("Gudhi not found: MapperComplex not available") + +############################################# +# Clustering ################################ +############################################# + +class MapperComplex(BaseEstimator, TransformerMixin): + """ + This is a class for computing Mapper simplicial complexes on point clouds or distance matrices. + """ + def __init__(self, filters, filter_bnds, colors, resolutions, gains, inp="point cloud", clustering=DBSCAN(), mask=0): + """ + Constructor for the MapperComplex class. + + Attributes: + inp (string): either "point cloud" or "distance matrix". Specifies the type of input data. + filters (numpy array of shape (num_points) x (num_filters)): filters (sometimes called lenses) used to compute the Mapper. Each column of the numpy array defines a scalar function defined on the input points. + filter_bnds (numpy array of shape (num_filters) x 2): limits of each filter, of the form [[f_1^min, f_1^max], ..., [f_n^min, f_n^max]]. If one of the values is numpy.nan, it can be computed from the points with the fit() method. + colors (numpy array of shape (num_points) x (num_colors)): functions used to color the nodes of the output Mapper simplicial complex. More specifically, coloring is done by computing the means of these functions on the subpopulations corresponding to each node. It can be the same as filters. + resolutions (numpy array of shape num_filters containing integers): resolution of each filter, ie number of intervals required to cover each filter image. + gains (numpy array of shape num_filters containing doubles in [0,1]): gain of each filter, ie overlap percentage of the intervals covering each filter image. + clustering (class): clustering class (default sklearn.cluster.DBSCAN()). Common clustering classes can be found in the scikit-learn library (such as AgglomerativeClustering for instance). + mask (int): threshold on the size of the Mapper nodes (default 0). Any node associated to a subpopulation with less than **mask** points will be removed. + + mapper_ (gudhi SimplexTree): Mapper simplicial complex computed after calling the fit() method + node_info_ (dictionary): various information associated to the nodes of the Mapper. + """ + self.filters, self.filter_bnds, self.resolutions, self.gains, self.colors, self.clustering = filters, filter_bnds, resolutions, gains, colors, clustering + self.input, self.mask = inp, mask + + def get_optimal_parameters_for_agglomerative_clustering(self, X, beta=0., C=10., N=100): + """ + Compute optimal scale and resolutions for a point cloud or a distance matrix. + + Parameters: + X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. + beta (double): exponent parameter (default 0.). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. + C (double): constant parameter (default 10.). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. + N (int): subsampling iterations (default 100). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. + + Returns: + delta (double): optimal scale that can be used with agglomerative clustering. + resolutions (numpy array of shape (num_filters): optimal resolutions associated to each filter. + """ + num_pts, num_filt, delta = X.shape[0], self.filters.shape[1], 0 + m = int( num_pts / np.exp((1+beta) * np.log(np.log(num_pts)/np.log(C))) ) + for _ in range(N): + subpop = np.random.choice(num_pts, size=m, replace=False) + if self.input == "point cloud": + d, _, _ = directed_hausdorff(X, X[subpop,:]) + if self.input == "distance matrix": + d = np.max(np.min(X[:,subpop], axis=1), axis=0) + delta += d/N + + pairwise = pairwise_distances(X, metric="euclidean") if self.input == "point cloud" else X + pairs = np.argwhere(pairwise <= delta) + num_pairs = pairs.shape[0] + res = [] + for f in range(num_filt): + F = self.filters[:,f] + minf, maxf = np.min(F), np.max(F) + resf = 0 + for p in range(num_pairs): + resf = max(resf, abs(F[pairs[p,0]] - F[pairs[p,1]])) + res.append(int((maxf-minf)/resf)) + + return delta, np.array(res) + + + def fit(self, X, y=None): + """ + Fit the MapperComplex class on a point cloud or a distance matrix: compute the Mapper and store it in a simplex tree called mapper_ + + Parameters: + X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. + y (n x 1 array): point labels (unused). + """ + num_pts, num_filters, num_colors = self.filters.shape[0], self.filters.shape[1], self.colors.shape[1] + + # If some resolutions are not specified, automatically compute them + if np.any(np.isnan(self.resolutions)): + delta, resolutions = self.get_optimal_parameters_for_agglomerative_clustering(X=X, beta=0., C=10, N=100) + #self.clustering = NNClustering(radius=delta, inp=self.input) + if self.input == "point cloud": + self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, affinity="euclidean") + else: + self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, affinity="precomputed") + self.resolutions = np.where(np.isnan(self.resolutions), resolutions, self.resolutions) + + # If some filter limits are unspecified, automatically compute them + self.filter_bnds = np.where(np.isnan(self.filter_bnds), np.hstack([np.min(self.filters, axis=0)[:,np.newaxis], np.max(self.filters, axis=0)[:,np.newaxis]]), self.filter_bnds) + + # Initialize attributes + self.mapper_, self.node_info_ = gd.SimplexTree(), {} + + # Compute which points fall in which patch or patch intersections + interval_inds, intersec_inds = np.empty(self.filters.shape), np.empty(self.filters.shape) + for i in range(num_filters): + f, r, g = self.filters[:,i], self.resolutions[i], self.gains[i] + min_f, max_f = self.filter_bnds[i,0], np.nextafter(self.filter_bnds[i,1], np.inf) + interval_endpoints, l = np.linspace(min_f, max_f, num=r+1, retstep=True) + intersec_endpoints = [] + for j in range(1, len(interval_endpoints)-1): + intersec_endpoints.append(interval_endpoints[j] - g*l / (2 - 2*g)) + intersec_endpoints.append(interval_endpoints[j] + g*l / (2 - 2*g)) + interval_inds[:,i] = np.digitize(f, interval_endpoints) + intersec_inds[:,i] = 0.5 * (np.digitize(f, intersec_endpoints) + 1) + + # Build the binned_data map that takes a patch or a patch intersection and outputs the indices of the points contained in it + binned_data = {} + for i in range(num_pts): + list_preimage = [] + for j in range(num_filters): + a, b = interval_inds[i,j], intersec_inds[i,j] + list_preimage.append([a]) + if b == a: + list_preimage[j].append(a+1) + if b == a-1: + list_preimage[j].append(a-1) + list_preimage = list(itertools.product(*list_preimage)) + for pre_idx in list_preimage: + try: + binned_data[pre_idx].append(i) + except KeyError: + binned_data[pre_idx] = [i] + + # Initialize the cover map, that takes a point and outputs the clusters to which it belongs + cover, clus_base = [[] for _ in range(num_pts)], 0 + + # For each patch + for preimage in binned_data: + + # Apply clustering on the corresponding subpopulation + idxs = np.array(binned_data[preimage]) + if len(idxs) > 1: + clusters = self.clustering.fit_predict(X[idxs,:]) if self.input == "point cloud" else self.clustering.fit_predict(X[idxs,:][:,idxs]) + elif len(idxs) == 1: + clusters = np.array([0]) + else: + continue + + # Collect various information on each cluster + num_clus_pre = np.max(clusters) + 1 + for clus_i in range(num_clus_pre): + node_name = clus_base + clus_i + subpopulation = idxs[clusters == clus_i] + if len(subpopulation) >= self.mask: + self.node_info_[node_name] = {} + self.node_info_[node_name]["indices"] = subpopulation + self.node_info_[node_name]["size"] = len(subpopulation) + self.node_info_[node_name]["colors"] = np.mean(self.colors[subpopulation,:], axis=0) + self.node_info_[node_name]["patch"] = preimage + + # Update the cover map + for pt in range(clusters.shape[0]): + node_name = clus_base + clusters[pt] + if clusters[pt] != -1 and self.node_info_[node_name]["size"] >= self.mask: + cover[idxs[pt]].append(node_name) + + clus_base += np.max(clusters) + 1 + + # Insert the simplices of the Mapper complex + for i in range(num_pts): + self.mapper_.insert(cover[i], filtration=-3) + self.mapper_.initialize_filtration() + + return self + + def compute_persistence_diagrams(self): + """ + Compute the extended persistence diagrams of the Mapper simplicial complex associated to each color function. + + Returns: + list_dgm (list of gudhi persistence diagrams): output extended persistence diagrams. There is one per color function. + """ + num_cols, list_dgm = self.colors.shape[1], [] + + # Compute an extended persistence diagram for each color + for c in range(num_cols): + + # Retrieve all color values + col_vals = {node_name: self.node_info_[node_name]["colors"][c] for node_name in self.node_info_.keys()} + + # Create a new simplicial complex by coning the Mapper with an extra point with name -2 + st = gd.SimplexTree() + list_simplices, list_vertices = self.mapper_.get_skeleton(1), self.mapper_.get_skeleton(0) + for (simplex, f) in list_simplices: + st.insert(simplex + [-2], filtration=-3) + + # Assign ascending filtration values on the original simplices and descending filtration values on the coned simplices + min_val, max_val = min(col_vals), max(col_vals) + for (vertex, f) in list_vertices: + if st.find(vertex): + st.assign_filtration(vertex, filtration = -2 + (col_vals[vertex[0]]-min_val)/(max_val-min_val)) + st.assign_filtration(vertex + [-2], filtration = 2 - (col_vals[vertex[0]]-min_val)/(max_val-min_val)) + + # Compute persistence + st.make_filtration_non_decreasing() + dgm = st.persistence() + + # Output extended persistence diagrams + for point in range(len(dgm)): + b,d = dgm[point][1][0], dgm[point][1][1] + b,d = min_val+(2-abs(b))*(max_val-min_val), min_val+(2-abs(d))*(max_val-min_val) + dgm[point] = tuple([dgm[point][0], tuple([b,d])]) + list_dgm.append(dgm) + + return list_dgm + + def compute_distribution(self, X, N=100): + """ + Compute a bootstrap distribution of bottleneck distances. More specifically, subsample the input point cloud or distance matrix, compute the Mapper with the same parameters on this subsample, and compare its extended persistence diagrams with the original ones. + + Parameters: + X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. + N (int): bootstrap iterations (default 100). + + Returns: + distribution: list of bottleneck distance values. + """ + num_pts, distribution = len(X), [] + dgm = self.compute_persistence_diagrams() + + for bootstrap_id in range(N): + + print(str(bootstrap_id) + "th iteration") + + # Randomly select points + idxs = np.random.choice(num_pts, size=num_pts, replace=True) + Xboot = X[idxs,:] if self.input == "point cloud" else X[idxs,:][:,idxs] + f_boot, c_boot = self.filters[idxs,:], self.colors[idxs,:] + Mboot = self.__class__(filters=f_boot, filter_bnds=self.filter_bnds, colors=c_boot, resolutions=self.resolutions, gains=self.gains, inp=self.input, clustering=self.clustering).fit(Xboot) + + # Compute the corresponding persistence diagrams + dgm_boot = Mboot.compute_persistence_diagrams() + + # Compute the bottleneck distances between them and keep the maximum + df = 0. + for i in range(len(dgm)): + npts, npts_boot = len(dgm[i]), len(dgm_boot[i]) + D1 = np.array([[dgm[i][pt][1][0], dgm[i][pt][1][1]] for pt in range(npts) if dgm[i][pt][0] <= 1]) + D2 = np.array([[dgm_boot[i][pt][1][0], dgm_boot[i][pt][1][1]] for pt in range(npts_boot) if dgm_boot[i][pt][0] <= 1]) + bottle = BottleneckDistance().fit([D1]) + df = max(df, float(np.squeeze(bottle.transform([D2])))) + distribution.append(df) + + return np.sort(distribution) -- cgit v1.2.3 From 7157d123ffd425ec104e0c468d24d48d7ce2fcd0 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Thu, 13 Jun 2019 17:52:38 -0400 Subject: added sklearn_tda examples --- src/cython/example/ex_clustering.py | 64 +++++++++++++++++++++ src/cython/example/ex_diagrams.py | 107 ++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/cython/example/ex_clustering.py create mode 100644 src/cython/example/ex_diagrams.py diff --git a/src/cython/example/ex_clustering.py b/src/cython/example/ex_clustering.py new file mode 100644 index 00000000..bee82414 --- /dev/null +++ b/src/cython/example/ex_clustering.py @@ -0,0 +1,64 @@ +import numpy as np +from sklearn.metrics import pairwise_distances +import os +import gudhi as gd +import sys +sys.path.append("../sktda/") +from clustering import * + +X = np.loadtxt("human") + +print("Mapper computation with point cloud") +mapper = MapperComplex(inp="point cloud", + filters=X[:,[2,0]], + filter_bnds=np.array([[np.nan,np.nan],[np.nan,np.nan]]), + resolutions=np.array([np.nan,np.nan]), gains=np.array([0.33,0.33]), + colors=X[:,2:3], + ).fit(X) + +f = open("mapper_pc", "w") +f.write("%s\n%s\n%s\n%f %f\n%d %d\n" % ("human", "coord2-0", "coord2", 10, 0.33, len(mapper.mapper_.get_skeleton(0)), len([edge for (edge,f) in mapper.mapper_.get_skeleton(1) if len(edge)==2]))) +for (vertex,_) in mapper.mapper_.get_skeleton(0): + f.write(str(vertex[0]) + " " + str(mapper.node_info_[vertex[0]]["colors"][0]) + " " + str(mapper.node_info_[vertex[0]]["size"]) + "\n") +for (edge,_) in mapper.mapper_.get_skeleton(1): + if len(edge) == 2: + f.write(str(edge[0]) + " " + str(edge[1]) + "\n") +f.close() + +os.system("python3 ~/Git/gudhi-devel/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py -f ~/Git/gudhi-devel/src/cython/example/mapper_pc") +os.system("rm ~/Git/gudhi-devel/src/cython/example/mapper_pc") + +dgms = mapper.compute_persistence_diagrams() +plot = gd.plot_persistence_diagram(dgms[0]) +plot.show() + +distrib = mapper.compute_distribution(X, N=10) +print("Distance threshold associated to confidence 90 percent is " + str(distrib[int(np.floor(0.9 * len(distrib)))])) + +print("Mapper computation with pairwise distances only") +X = pairwise_distances(X) +mapper = MapperComplex(inp="distance matrix", + filters=X[:,[2,0]], + filter_bnds=np.array([[np.nan,np.nan],[np.nan,np.nan]]), + resolutions=np.array([np.nan,np.nan]), gains=np.array([0.33,0.33]), + colors=np.max(X, axis=1)[:,np.newaxis], + ).fit(X) + +f = open("mapper_dm", "w") +f.write("%s\n%s\n%s\n%f %f\n%d %d\n" % ("human", "coord2-0", "coord2", 10, 0.33, len(mapper.mapper_.get_skeleton(0)), len([edge for (edge,f) in mapper.mapper_.get_skeleton(1) if len(edge)==2]))) +for (vertex,_) in mapper.mapper_.get_skeleton(0): + f.write(str(vertex[0]) + " " + str(mapper.node_info_[vertex[0]]["colors"][0]) + " " + str(mapper.node_info_[vertex[0]]["size"]) + "\n") +for (edge,_) in mapper.mapper_.get_skeleton(1): + if len(edge) == 2: + f.write(str(edge[0]) + " " + str(edge[1]) + "\n") +f.close() + +os.system("python3 ~/Git/gudhi-devel/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py -f ~/Git/gudhi-devel/src/cython/example/mapper_dm") +os.system("rm ~/Git/gudhi-devel/src/cython/example/mapper_dm") + +dgms = mapper.compute_persistence_diagrams() +plot = gd.plot_persistence_diagram(dgms[0]) +plot.show() + +distrib = mapper.compute_distribution(X, N=10) +print("Distance threshold associated to confidence 90 percent is " + str(distrib[int(np.floor(0.9 * len(distrib)))])) diff --git a/src/cython/example/ex_diagrams.py b/src/cython/example/ex_diagrams.py new file mode 100644 index 00000000..aee20245 --- /dev/null +++ b/src/cython/example/ex_diagrams.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +from sklearn.kernel_approximation import RBFSampler + +import sys +sys.path.append("../sktda/") +from preprocessing import * +from metrics import * +from vector_methods import * +from kernel_methods import * + +D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.]]) +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,10.],[0.,10.]) +plt.show() + +diags = [D] + +LS = Landscape(resolution = 1000) +L = LS.fit_transform(diags) +plt.plot(L[0][:1000]) +plt.plot(L[0][1000:2000]) +plt.plot(L[0][2000:3000]) +plt.show() + +def pow(n): + return lambda x: np.power(x[1]-x[0],n) + +SH = Silhouette(resolution=1000, weight=pow(2)) +sh = SH.fit_transform(diags) +plt.plot(sh[0]) +plt.show() + +BC = BettiCurve(resolution=1000) +bc = BC.fit_transform(diags) +plt.plot(bc[0]) +plt.show() + +CP = ComplexPolynomial(threshold=-1, F="T") +cp = CP.fit_transform(diags) +print("Complex polynomial is " + str(cp[0,:])) + +TV = TopologicalVector(threshold=-1) +tv = TV.fit_transform(diags) +print("Topological vector is " + str(tv[0,:])) + +diagsT = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(diags) +PI = PersistenceImage(bandwidth=1., weight=lambda x: x[1], im_range=[0,10,0,10], resolution=[100,100]) +pi = PI.fit_transform(diagsT) +plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) +plt.show() + +plt.scatter(D[:,0],D[:,1]) +D = np.array([[1.,5.],[3.,6.],[2.,7.]]) +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,10.],[0.,10.]) +plt.show() + +diags2 = [D] + +def arctan(C,p): + return lambda x: C*np.arctan(np.power(x[1], p)) + +PWG = PersistenceWeightedGaussianKernel(bandwidth=1., kernel_approx=None, weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("PWG kernel is " + str(Y[0][0])) + +PWG = PersistenceWeightedGaussianKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2])), weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("Approximate PWG kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(bandwidth=1.) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("PSS kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("Approximate PSS kernel is " + str(Y[0][0])) + +sW = SlicedWassersteinDistance(num_directions=100) +X = sW.fit(diags) +Y = sW.transform(diags2) +print("SW distance is " + str(Y[0][0])) + +SW = SlicedWassersteinKernel(num_directions=100, bandwidth=1.) +X = SW.fit(diags) +Y = SW.transform(diags2) +print("SW kernel is " + str(Y[0][0])) + +W = BottleneckDistance(epsilon=.001) +X = W.fit(diags) +Y = W.transform(diags2) +print("Bottleneck distance is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1.) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("PF kernel is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1., kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("Approximate PF kernel is " + str(Y[0][0])) -- cgit v1.2.3 From 3cf724a855cef4f47e5cdbb2a0ff2b6b093c6b87 Mon Sep 17 00:00:00 2001 From: mathieu Date: Fri, 14 Jun 2019 10:31:19 -0400 Subject: added human file and updated path in ex_clustering.py --- data/points/human | 4706 +++++++++++++++++++++++++++++++++++ src/cython/example/ex_clustering.py | 2 +- 2 files changed, 4707 insertions(+), 1 deletion(-) create mode 100644 data/points/human diff --git a/data/points/human b/data/points/human new file mode 100644 index 00000000..feaa0dd1 --- /dev/null +++ b/data/points/human @@ -0,0 +1,4706 @@ +0.144529 -0.014328 0.381773 +-0.15378 -0.045707 0.361045 +-0.153917 -0.057275 0.371308 +-0.172725 0.007 0.492798 +-0.119664 0.061296 0.365349 +0.128166 -0.068572 0.432762 +0.089198 0.014048 -0.447694 +0.075569 0.021145 -0.417112 + 0.098403 0.03188 -0.416332 +-0.135518 -0.031058 -0.793657 +-0.155207 -0.043962 -0.867084 +-0.155561 -0.039537 -0.782486 +0.047747 0.043681 0.604458 +0.01706 0.058681 0.583267 +0.153119 -0.065476 -0.465974 +0.134237 -0.06509 -0.429525 +0.138009 -0.083138 -0.463111 +0.061936 0.021354 -0.365004 +-0.327282 0.105737 -0.088733 +-0.333168 0.111481 -0.099092 +0.02915 0.038215 0.809093 +0.000855 0.039818 0.813241 +0.188052 0.020848 -0.942916 +0.200327 0.058375 -0.947736 +0.170213 0.052306 -0.931828 +-0.051119 -0.110776 0.111316 +-0.024531 -0.111686 0.078082 +-0.021008 -0.089762 0.10657 +0.158999 0.015347 -0.916402 +0.127084 0.053037 -0.930089 +0.110535 0.0176 -0.450151 +0.12031 0.025078 -0.419837 +0.125486 0.007107 -0.444987 +0.087186 -0.068787 -0.428022 +0.068096 -0.048487 -0.423994 +0.074575 -0.071291 -0.460151 +-0.129151 -0.031845 0.547791 +-0.092828 -0.001702 0.544926 +-0.146897 -0.021057 0.536567 +-0.040908 -0.080471 0.144988 +-0.070348 -0.092228 0.142231 +0.135184 -0.05792 -0.366185 +0.150502 -0.030301 -0.366514 +0.154969 -0.053619 -0.297939 +-0.046419 0.016608 0.551426 +-0.140395 -0.04129 0.102368 +-0.15361 -0.023507 0.066896 +-0.143445 -0.07334 0.071094 +-0.150698 -0.083179 0.012934 +-0.164552 -0.040183 0.016637 +-0.172922 -0.056311 -0.054118 +0.142078 0.114992 0.400106 +0.136581 0.121451 0.422073 +0.118363 -0.052599 -0.969187 +0.110299 -0.090045 -0.973743 +0.106227 -0.067834 -0.951593 +-0.075572 -0.055159 0.227875 +-0.048656 -0.08199 0.316154 +-0.088566 -0.065864 0.297905 +-0.175443 0.052256 -0.931828 +-0.142719 0.087605 -0.938697 +-0.186188 0.080344 -0.942939 +-0.281944 -0.02183 0.181925 +-0.282953 -0.017408 0.129994 +-0.25389 -0.034093 0.168434 +-0.092047 -0.041549 0.557254 +-0.085291 -0.062035 0.558917 +-0.302715 0.009876 0.086968 +-0.282908 -0.00097 0.077127 +-0.117228 0.023298 0.342619 +-0.177404 -0.012747 -0.051392 +-0.272247 -0.020141 0.233158 +-0.254475 -0.039985 0.216094 +-0.017864 0.070352 0.178973 +-0.003695 0.067376 0.163481 +0.030308 0.031315 0.603728 +0.02661 0.115199 0.744608 +0.033244 0.11394 0.710348 +0.012279 0.123046 0.708533 +-0.05487 -0.030769 0.660855 +-0.04444 -0.039595 0.603866 +0.027038 -0.082087 0.737178 +0.023403 -0.083864 0.708068 +0.044858 -0.07186 0.710449 +-0.141696 0.005473 0.082272 +-0.322537 0.044383 -0.093982 +0.137584 0.087655 -0.938697 +0.110404 0.089056 -0.947375 +0.201593 0.059307 -0.975093 +0.19232 0.094332 -0.975768 +0.209811 0.077354 -0.962319 +0.008617 -0.099163 0.047708 +0.005395 -0.079622 0.076357 +0.106371 0.041851 -0.959546 +0.116565 0.024136 -0.939902 +0.10789 0.05867 -0.947567 +0.117802 0.064238 0.011831 +0.099654 0.053985 0.060307 +0.12682 0.046382 0.064253 +0.14171 0.005468 0.082276 +0.162647 0.005991 0.019937 +0.146819 0.043902 0.018713 +-0.086735 -0.081196 0.550877 +-0.142522 -0.064292 0.541934 +-0.101551 -0.092019 0.536874 +-0.147816 -0.090727 -0.053942 +0.089669 -0.075237 -0.296805 +0.110958 -0.094262 -0.219465 +0.067718 -0.086145 -0.213647 +0.035525 -0.10073 0.457763 +0.000152 -0.097146 0.473706 +0.000252 -0.080555 0.318839 +0.16693 0.014297 -0.292232 +0.165909 0.033768 -0.213443 +0.177974 -0.006912 -0.216479 +0.122756 0.049674 -0.332788 +0.11496 0.064838 -0.287502 +0.148695 0.045817 -0.290571 +0.055746 -0.030683 0.660961 +0.033546 -0.066936 0.676952 +0.017439 -0.069226 0.625575 +0.039048 0.038642 -0.121677 +0.035134 0.012056 -0.055409 +0.054628 0.051615 -0.046746 +-0.219382 0.038108 0.176265 +-0.218586 0.03396 0.225697 +-0.200001 0.012939 0.213512 +0.134152 -0.008037 -0.466849 +-0.111507 0.041802 -0.959546 +-0.105326 0.074882 -0.964246 +-0.113025 0.05862 -0.947567 +-0.054482 -0.034932 0.574847 +-0.090128 -0.021415 0.553574 +-0.116313 -0.048454 0.13681 +-0.09038 -0.053234 0.172229 +-0.105186 -0.014928 0.166671 +-0.126942 0.046414 0.064231 +-0.116088 0.034063 0.10285 +0.078883 0.038902 -0.375952 +0.08771 0.046925 -0.353531 +0.166615 -0.02867 -0.506862 +0.15046 -0.019487 -0.47215 +0.156742 -0.040331 -0.46813 +0.049615 0.064657 0.606895 +-0.093081 0.017267 0.197077 +-0.107564 -0.001066 0.279921 +-0.087365 0.043211 0.25934 +-0.229315 0.033998 0.125778 +-0.248089 0.054546 0.138866 +0.106254 -0.066109 -0.3667 +0.125371 -0.075616 -0.30084 +-0.035641 -0.100747 0.457937 +-0.095086 -0.103368 0.460305 +0.018598 0.108161 0.632002 +0.008009 0.110019 0.630723 +-0.145172 -0.126207 -0.962029 +-0.147995 -0.115103 -0.901861 +-0.165635 -0.112794 -0.963402 +0.181052 0.080394 -0.942939 +-0.037409 0.016634 -0.022456 +-0.054935 0.051562 -0.047076 +-0.051074 0.0426 0.012895 +0.024454 -0.043608 -0.124411 +0.030596 -0.06583 -0.057455 +0.026133 -0.020196 -0.061267 +0.004091 -0.07642 0.016114 +-0.143275 0.11508 -0.950869 +-0.10902 0.06955 0.488685 +-0.119427 0.028724 0.512181 +-0.077684 0.050975 0.507111 +0.03316 -0.09173 0.54611 +-0.126953 0.020019 -0.968705 +-0.153991 -0.027525 -0.970453 +-0.162328 0.037423 -0.975575 +0.019284 0.112865 0.690206 +-0.017888 0.004075 0.813135 +-0.036857 0.015351 0.806263 +-0.019684 -0.022438 0.806286 +-0.118067 0.060321 -0.975334 +-0.11084 -0.089559 0.388201 +-0.136103 -0.098275 0.477748 +-0.068592 0.049409 0.668449 +-0.354522 0.111955 -0.119529 +0.133555 0.0107 -0.91375 +0.152919 -0.021289 -0.903386 +-0.047277 0.088388 0.477505 +0.028943 0.100045 0.614915 +-0.178297 -0.027228 -0.973164 +-0.195107 0.015542 -0.975093 +-0.009711 -0.053847 -0.021292 +-0.020989 -0.095217 -0.015669 +-0.206728 0.059258 -0.975093 +-0.160538 0.080674 -0.978019 +-0.028046 0.017736 0.594311 +-0.099769 0.054021 0.060283 +0.292938 0.024667 0.005698 +0.281872 0.042386 0.010061 +0.293815 0.043479 -0.024205 +0.253755 -0.042591 0.24146 +0.236374 -0.050375 0.26034 +0.2334 -0.049604 0.232122 +-0.041908 -0.013527 0.798038 +-0.169026 -0.017468 -0.554275 +-0.180719 -0.036864 -0.610601 +-0.177641 -0.046876 -0.552508 +0.142302 -0.064331 0.542099 +0.091978 -0.041556 0.557259 +0.085357 -0.061971 0.558866 +0.163846 -0.07001 -0.134881 +0.147918 -0.090765 -0.053915 +0.128677 -0.096438 -0.134035 +0.033866 0.08705 0.595856 +-0.061852 0.08285 0.67803 +-0.064405 0.0745 0.705228 +-0.057315 0.087943 0.688282 +0.026825 0.058063 0.011471 +0.00959 0.046503 -0.022489 +7.5e-05 0.067016 0.012148 +0.037289 0.016594 -0.022429 +0.027248 0.032898 -0.019611 +0.051064 0.042566 0.012814 +0.172917 -0.056346 -0.054093 +0.177533 -0.01278 -0.051369 +0.165297 -0.040174 0.016676 +-0.049827 -0.001275 0.628239 +-0.046082 -0.007732 0.588234 +0.027282 0.017653 0.594293 +0.218467 0.03392 0.225725 +0.218303 0.022488 0.260748 +0.246836 0.034226 0.236039 +0.180761 -0.032705 -0.133588 +-0.061259 -0.067857 -0.496632 +-0.054909 -0.056275 -0.544951 +-0.05886 -0.036687 -0.498712 +-0.096128 0.07647 0.350771 +-0.125425 -0.075572 -0.300885 +-0.089749 -0.075321 -0.29683 +-0.111609 -0.094226 -0.21961 +-0.059916 -0.025146 -0.5475 +-0.070274 -0.011925 -0.501404 +-0.014861 0.085999 0.475193 +0.011944 0.087545 0.471872 +-0.013265 0.101124 0.440331 +-0.111865 -0.105085 -0.952919 +-0.111362 -0.067884 -0.951593 +-0.108817 -0.085906 -0.928478 +0.129068 -0.032432 0.547796 +0.092747 -0.004496 0.54493 +-0.154642 0.003294 -0.352704 +-0.142743 0.028986 -0.337671 +-0.069935 -0.024902 -0.463284 +-0.315481 0.074492 -0.075996 +-0.325874 0.058528 -0.097514 +-0.332121 0.079166 -0.097184 +0.065756 0.113544 0.355893 +0.099174 0.128438 0.362935 +0.091993 0.102855 0.353431 +0.150875 -0.083168 0.013016 +-0.108399 -0.048104 0.306063 +-0.047271 0.099741 0.356288 +-0.066219 0.088165 0.321493 +-0.035225 -0.092767 0.543027 +-0.009859 0.104972 0.60128 +-0.043422 -0.067033 0.234382 +-0.197455 0.094282 -0.975768 +-0.104462 -0.111554 -0.498426 +-0.078262 -0.094503 -0.497102 +-0.092157 -0.088636 -0.463068 +-0.146801 -0.077454 -0.220163 +0.086804 -0.081061 0.550911 +0.134521 0.11988 0.378405 +0.118328 0.120392 0.364735 +0.115393 0.14537 0.390518 +-0.054843 -0.015197 0.563407 +-0.05504 0.025 0.792306 +-0.070388 0.032156 0.769815 +-0.063682 -0.006236 0.777538 +-0.000626 0.114562 0.672841 +0.253835 -0.01734 0.119652 +0.223827 -0.021655 0.161215 +0.231867 0.004314 0.117834 +-0.114756 0.089415 -0.973165 +-0.127269 0.107695 -0.974322 +0.096751 0.074035 -0.05055 +0.078066 0.059374 0.009685 +0.000726 0.11622 0.656618 +0.165173 0.032563 -0.050176 +0.000689 -0.080625 0.101576 +-0.005268 -0.079602 0.076344 +0.183917 -0.053008 0.530657 +0.17545 -0.080203 0.523558 +0.141167 -0.093629 0.517571 +-0.051253 0.096483 0.711906 +-0.022715 -0.04717 0.792329 +-0.047913 -0.041404 0.777538 +0.052291 0.096624 0.712047 +-0.026424 -0.020287 -0.061717 +-0.035397 0.011988 -0.055788 +-0.024475 -0.015906 -0.03417 +-0.062564 0.085644 0.653961 +0.006432 -0.067984 0.669362 +-0.091524 -0.120719 -0.548864 +-0.352035 0.051345 -0.126219 +-0.078178 0.050942 0.154885 +-0.07227 0.047244 0.203935 +-0.048281 0.062448 0.195881 +0.155341 0.052205 -0.127805 +0.177243 0.012794 -0.130554 +0.072937 0.07156 -0.124618 +0.118217 0.146923 0.421907 +0.020198 0.076819 0.079276 +0.139978 0.062527 -0.211926 +-0.089177 -0.111241 0.111846 +-0.09518 -0.081905 0.14393 +-0.074495 -0.071296 -0.460179 +-0.042869 0.071572 0.14139 +0.00073 -0.05162 -0.010499 +-0.004172 -0.07642 0.016012 +-0.131034 -0.082145 0.442399 +-0.100523 -0.027685 0.22648 +-0.015731 -0.07176 0.615875 +0.119466 0.075689 -0.126799 +-0.132503 -0.106466 -0.501087 +-0.122962 -0.130064 -0.552684 +-0.074738 0.044114 0.706666 +-0.066077 0.071903 0.732644 +-0.062916 0.068521 0.757684 +-0.048496 0.098136 0.740433 +-0.033166 0.000252 -0.280952 +-0.048184 0.017041 -0.328096 +-0.048173 0.0348 -0.27966 +-0.025707 -0.068824 0.768329 +-0.166577 0.1103 -0.974177 +-0.124674 -0.051241 -0.914051 +-0.112361 -0.082599 -0.886027 +-0.08053 -0.007338 -0.458902 +-0.068099 -0.00941 -0.416012 +-0.214946 0.077303 -0.962319 +-0.205462 0.058324 -0.947736 +0.138907 0.065218 -0.050517 +0.199882 0.012901 0.213538 +0.200105 0.01313 0.252288 +0.055229 0.054786 -0.204024 +-0.024533 0.091239 0.330841 +-0.172277 -0.081982 -0.551109 +-0.167673 -0.057628 -0.50493 +-0.058987 -0.06452 -0.289445 +-0.068156 -0.086411 -0.213966 +0.254357 -0.040025 0.216122 +-0.067674 0.042176 -0.330429 +-0.076952 0.060866 -0.28229 +0.013977 0.113422 0.640177 +-6.9e-05 0.086527 0.3376 +0.019179 0.079744 0.281051 +-0.015722 0.079249 0.274606 +0.115072 -0.070048 -0.42498 +0.077551 -0.065157 -0.362486 +0.033381 0.053515 0.505504 +0.054667 0.033265 0.526511 +0.077572 0.050943 0.507134 +0.086553 0.046191 0.265442 +0.059227 0.073277 0.277063 +0.088572 0.057052 0.304907 +0.057158 -0.046385 -0.353944 +-0.056639 0.061106 0.779722 +-0.043816 0.051377 0.797042 +-0.031526 0.076752 0.792306 +0.171482 -0.044588 -0.21885 +-0.143382 0.00154 -0.554016 +-0.164175 -0.011574 -0.613396 +0.045344 -0.080279 -0.128946 +-0.043257 0.090499 0.76982 +-0.078577 0.001976 0.744204 +-0.07502 -0.017208 0.703979 +-0.068733 -0.033112 0.745947 +-0.009464 0.029546 0.541617 +-0.050437 -0.062276 0.744451 +-0.078293 0.038525 0.740162 +0.024463 0.091212 0.330858 +-0.112377 0.005125 -0.552117 +-0.131688 -0.001292 -0.614531 +-0.183022 -0.043595 -0.957618 +-0.196997 -0.004429 -0.960993 +0.240756 0.018538 0.268716 +0.095305 0.074055 -0.207363 +0.056798 0.020951 0.627455 +0.062692 0.009616 0.652266 +0.049787 -0.001444 0.628412 +0.146665 -0.077493 -0.22013 +-0.037163 -0.033757 -0.284242 +-0.038849 -0.058215 -0.209167 +-0.065469 -0.047111 -0.461306 +0.047178 0.099707 0.356312 +0.308841 0.028975 0.094589 +0.301247 0.005445 0.09387 +0.30888 0.029103 0.019447 +0.017844 0.071064 0.186113 +-0.155734 -0.088774 -0.50245 +-0.152652 -0.115873 -0.552632 +0.011229 0.02562 -0.035193 +-5.3e-05 0.023896 -0.036713 +-0.088766 0.057083 0.305007 +-0.120964 0.004341 -0.951472 +-0.115539 0.089006 -0.947375 +0.292644 0.072606 -0.014141 +-0.123809 0.004777 -0.499938 +0.000277 -0.080877 0.691549 +-0.034692 0.087566 0.596768 +0.103311 0.149984 0.391528 +0.078795 0.134336 0.365284 +0.135882 -0.098348 0.47797 +0.094456 -0.103522 0.46077 +-0.134073 -0.008009 -0.466876 +-0.142742 -0.028419 -0.439643 +-0.106323 -0.066074 -0.366736 +-0.080573 -0.002643 -0.550185 +-0.099477 -0.002733 -0.614967 +-0.329349 0.034688 -0.054928 +-0.342692 0.069348 -0.059398 +-0.347817 0.042787 -0.102173 +0.000763 0.044977 0.590746 +1.7e-05 0.025278 0.592935 +-0.029687 0.031244 0.60365 +-0.053206 0.088406 0.622436 +0.059948 -0.063445 -0.289438 +-0.127417 -0.115851 -0.974611 +-0.121738 -0.118767 -0.954292 +-0.151626 -0.006646 -0.507398 +-0.154614 -0.053562 -0.298047 +-0.141938 -0.093481 0.516984 +-0.165744 -0.028673 -0.506889 +-0.133184 0.029585 -0.373943 +-0.146871 0.043935 0.01869 +-0.161897 0.006001 0.019931 +0.039669 -0.057032 -0.208807 +-0.028046 0.038112 0.808937 +-4.6e-05 -0.020914 -0.03562 +-0.071917 0.145224 0.383713 +-0.078914 0.134376 0.365257 +-0.103429 0.150023 0.391501 +-0.016033 0.0586 0.583186 +-0.150342 -0.019487 -0.472176 +-0.058188 -0.098083 -0.059091 +-0.030917 -0.065889 -0.057781 +-0.0438 -0.102119 -0.030128 +-0.099293 0.128477 0.362907 +-0.092113 0.102894 0.353403 +-0.118447 0.120432 0.364707 +-0.0642 0.142063 0.402239 +0.044609 -0.039585 0.604051 +-0.168519 -0.098024 -0.977937 +-0.179056 -0.08227 -0.963161 +-0.122882 -0.110201 -0.896682 +-0.156663 -0.040336 -0.468156 +0.107748 -0.105176 -0.056081 +0.1285 -0.039664 0.355589 +-0.158194 -0.021338 -0.903386 +-0.164797 0.015297 -0.916402 +-0.176744 -0.016583 -0.931828 +0.033356 0.125884 0.406133 +0.010034 0.099979 0.392826 +0.01712 0.102705 0.44066 +0.024443 -0.015939 -0.034147 +0.009626 -0.053851 -0.021289 +0.042447 0.121595 0.37503 +-0.347781 0.05466 -0.105101 +-0.115513 0.145409 0.39049 +0.111316 -0.089478 0.388386 +-0.128517 -0.00972 0.345704 +0.080848 0.011758 0.692945 +-0.067863 -0.031032 -0.431189 +-0.118337 0.146961 0.421881 +0.045869 -0.069312 0.571091 +-0.126084 0.090514 0.363964 +-0.356352 0.06532 -0.102173 +-0.281744 0.042127 0.010238 +-0.292665 0.041989 -0.023182 +-0.2916 0.071244 -0.013206 +-0.016178 0.060644 0.806268 +-0.34744 0.048022 -0.151125 +-0.114149 0.105438 -0.967742 +-0.162121 -0.095588 0.45288 +-0.114846 -0.090481 -0.461001 +-0.087198 -0.068891 -0.428048 +0.042983 -0.102146 -0.030109 +0.023895 -0.066464 -0.037126 +0.045982 -0.007651 0.589677 +-0.13249 -0.021343 -0.963114 +-0.12112 -0.031941 -0.947976 +-0.123497 -0.052648 -0.969187 +-0.173337 -0.062024 -0.975816 +-0.212735 0.034612 -0.964126 +-0.13793 -0.083142 -0.463138 +-0.032206 0.113799 0.710208 +-0.018056 0.112723 0.690066 +-0.153041 -0.06548 -0.466002 +-0.084204 -0.118301 -0.016723 +-0.107774 -0.105145 -0.056224 +-0.357731 0.118728 -0.141076 +-0.012173 0.100156 0.388899 +-0.042563 0.12163 0.375005 +-0.033484 0.125919 0.406108 +0.00895 0.029531 0.541612 +0.034096 0.103917 0.67485 +0.316842 0.076237 -0.077194 +0.323899 0.046128 -0.095179 +0.025035 -0.017397 -0.204731 +0.022904 -0.002982 -0.119922 +-0.017702 0.108056 0.631897 +-0.090769 0.050144 0.086125 +-0.071768 0.049796 0.05205 +0.058343 0.098285 0.662255 +0.062548 0.085885 0.653903 +0.047412 0.103597 0.643011 +-0.356011 0.072244 -0.110379 +-0.094694 0.016232 0.53498 +0.031138 0.0202 -0.202722 +0.023257 0.108698 0.644556 +0.298563 0.049907 0.106045 +0.309127 0.055705 0.032301 +-0.127245 0.125928 0.44046 +-0.107979 0.133071 0.444265 +0.002793 0.120311 0.643807 +-0.139345 0.01065 -0.91375 +0.191354 -0.010656 0.245307 +0.000195 -0.089014 0.717793 +-0.022347 -0.083906 0.707302 +-0.033776 0.053541 0.505486 +0.0076 0.108722 0.620553 +0.009016 0.106372 0.608066 +0.05338 0.099889 0.672242 +0.247319 0.050332 0.189783 +0.277263 0.037855 0.19703 +0.066826 0.030362 0.643759 +0.068832 0.018953 0.660837 +-0.05508 0.054469 -0.2044 +-0.095081 0.07404 -0.207566 +-0.121146 -0.113881 0.007467 +-0.007983 -0.067736 0.665032 +-0.139878 0.062566 -0.211961 +-0.148273 0.045873 -0.29067 +-0.165897 0.033789 -0.213473 +-0.041772 0.121934 0.43798 +0.109205 -0.048064 0.306801 +-0.094235 0.003354 -0.498127 +0.277358 0.054616 0.14883 +0.247971 0.054507 0.138894 +0.15507 0.003229 -0.352581 +0.143146 0.028923 -0.337551 +0.349178 0.044531 -0.10337 +-0.134886 -0.057869 -0.366286 +0.301726 0.003593 0.142683 +0.299445 0.033456 0.150221 +0.291556 0.007949 0.193786 +-0.36146 0.095938 -0.122006 +-0.292738 0.024324 0.005932 +0.215633 0.016904 0.307366 +0.333478 0.080898 -0.098369 +-0.354136 0.088244 -0.126082 +-0.024054 -0.018703 -0.205089 +-0.030282 0.019018 -0.203081 +-0.023349 -0.003622 -0.121357 +-0.009637 0.122749 0.70621 +0.162028 -0.090011 -0.885108 +0.165776 -0.102924 -0.790685 +0.143895 -0.115061 -0.901856 +0.051724 0.088579 0.622321 +0.061724 0.063033 0.633357 +0.06924 0.049621 0.668534 +0.35772 0.067034 -0.103388 +0.214633 -0.048801 0.252022 +0.135277 -0.031016 -0.794097 +0.151255 -0.043483 -0.866807 +0.130025 -0.042014 -0.864241 +-0.027974 0.006868 -0.029505 +-0.024899 -0.044248 -0.125845 +0.142675 -0.028427 -0.439606 +0.146143 -0.047721 -0.430244 +-0.344331 0.038782 -0.132348 +-0.033038 0.103786 0.67472 +-0.125521 0.007147 -0.445015 +-0.110456 0.017639 -0.450178 +-0.177355 0.012827 -0.130577 +-0.177958 -0.006894 -0.216507 +0.020331 -0.09522 -0.015547 +-0.047532 0.103303 0.641395 +-0.028376 0.100234 0.614667 +-0.134303 -0.065085 -0.42956 +-0.354421 0.12716 -0.151317 +-0.348795 0.121213 -0.142322 +0.092382 0.046406 0.119475 +0.116004 0.034072 0.102858 +0.090667 0.050145 0.086144 +-0.13464 0.11992 0.378378 +-0.145812 0.082925 0.387467 +-0.142197 0.115031 0.400078 +-0.073683 0.142378 0.425002 +-0.11906 0.10213 0.463337 +-0.137325 0.059317 0.473022 +-0.216529 -0.057972 0.41774 +-0.196262 -0.081978 0.459561 +-0.211776 -0.046564 0.468374 +0.179611 -0.005892 0.290372 +0.072121 0.033447 0.670515 +-0.114173 0.06488 -0.287538 +0.086455 -0.101061 0.513489 +-0.148602 0.042639 0.439711 +-0.122607 0.04972 -0.332836 +-0.13875 0.086783 0.448968 +-0.166519 0.014354 -0.292347 +0.089044 -0.065899 0.298075 +0.100498 -0.027722 0.226627 +0.048519 -0.014071 -0.352485 +0.038536 -0.03231 -0.284242 +0.000144 -0.076816 0.142161 +-0.067966 -0.048732 -0.424016 +0.046995 0.088353 0.477529 +0.088208 0.107273 0.467101 +0.061381 0.118752 0.456477 +0.193747 0.011959 0.298081 +0.034539 0.001698 -0.280952 +0.048886 0.018356 -0.328229 +-0.000763 0.058573 0.503145 +-0.064384 0.02103 -0.364348 +-0.079524 0.038923 -0.375843 +-0.088095 0.046844 -0.35346 +0.279857 0.058875 0.019882 +0.256561 0.051897 0.087347 +0.293923 0.07157 0.020631 +-0.025409 0.115069 0.74448 +0.001008 0.120894 0.74601 +0.024428 -0.118227 0.018318 +0.045667 -0.121959 -0.007542 +0.252255 0.033409 0.076695 +-0.158045 -0.017034 0.358421 +-0.15663 -0.037984 0.35548 +-0.148633 0.077503 0.419924 +-0.180872 -0.032671 -0.13361 +-0.17169 -0.094525 0.495191 +-0.196785 -0.069355 0.503 +-0.136701 0.121491 0.422046 +-0.088328 0.107312 0.467073 +-0.356358 0.050663 -0.148806 +-0.129249 -0.096403 -0.134181 +-0.163998 -0.069974 -0.134907 +-0.150151 -0.030244 -0.366624 +0.062655 -0.048958 0.705925 +0.081642 -0.002642 -0.550158 +0.095049 0.003325 -0.498101 +0.113446 0.005123 -0.552089 +-0.17147 -0.044567 -0.218881 +-0.356494 0.095415 -0.106381 +-0.081569 -0.101578 -0.133396 +0.359248 0.088392 -0.100782 +-0.046231 -0.080746 -0.130322 +0.041642 0.121894 0.438006 +0.129402 -0.009686 0.345732 +0.201901 -0.019149 0.207794 +0.209533 0.008252 0.16446 +-0.329022 0.034296 -0.095499 +-0.065874 0.113583 0.355867 +0.075559 -0.055198 0.227902 +0.061849 -0.069654 0.179403 +0.089743 -0.053273 0.172255 +-0.115191 -0.070046 -0.425006 +-0.115434 -0.090095 -0.973743 +0.278928 0.059992 0.097192 +0.113573 -0.062643 -0.871804 +0.101579 -0.052474 -0.79698 +0.124623 0.004748 -0.499911 +0.161939 -0.11913 -0.696457 +0.136289 -0.130094 -0.703771 +0.149847 -0.119757 -0.783024 +0.225358 -0.039917 0.205755 +0.071144 -0.011928 -0.501376 +0.080559 -0.007372 -0.458874 +0.344051 0.071092 -0.060589 +-0.357919 0.086664 -0.099675 +-0.061593 -0.049099 0.705784 +-0.032553 -0.067034 0.676746 +-0.077423 -0.065374 -0.362509 +0.105019 -0.014965 0.166697 +0.212039 -0.041769 0.226301 +0.113461 -0.126095 -0.709147 +0.12885 -0.123528 -0.796839 +-0.047846 -0.015163 -0.352407 +0.119073 0.044154 -0.348785 +0.116024 -0.048486 0.136832 +0.345781 0.117308 -0.104241 +0.351121 0.109018 -0.094476 +0.052426 -0.056817 0.574477 +0.095396 -0.072379 -0.798043 +-0.328432 0.095191 -0.091348 +-0.061499 0.118792 0.456451 +-0.056158 -0.047468 -0.353951 +-0.069627 0.003538 -0.420243 +-0.094278 0.054827 -0.332642 +0.31826 0.03996 -0.016181 +-0.043757 -0.071989 0.710178 +0.105996 0.001263 0.27446 +0.076423 0.042651 0.208272 +-0.116755 -0.036153 -0.785482 +-0.134088 -0.042806 -0.864056 +0.253771 -0.034132 0.168461 +0.155978 -0.039528 -0.782461 +0.169688 -0.057425 -0.788607 +-0.009594 0.046517 -0.022378 +0.152497 -0.006648 -0.507371 +-0.342275 0.113004 -0.121218 +-0.328432 0.10443 -0.05057 +-0.333615 0.114459 -0.087733 +-0.349759 0.107273 -0.09328 +-0.087764 0.135617 0.442196 +0.162192 -0.06653 -0.871875 +0.174311 -0.078585 -0.788353 +-0.089218 0.014086 -0.447721 +0.197414 -0.033855 0.245837 +0.094128 -0.081944 0.143956 +-0.169532 -0.022453 -0.295043 +0.01708 0.100498 0.598854 +0.093526 0.014904 0.191271 +0.282834 -0.017447 0.13002 +0.282789 -0.00101 0.077153 +0.108091 -0.083728 -0.886243 +0.114925 -0.090476 -0.460974 +0.229196 0.03396 0.125804 +-0.011346 0.025647 -0.035213 +0.062826 0.08297 0.678076 +-0.172061 -0.002328 0.335921 +-0.169846 -0.00047 0.366421 +0.09249 -0.103709 -0.710652 +0.112314 -0.116058 -0.788678 +0.281825 -0.02187 0.181952 +0.272129 -0.02018 0.233186 +0.269184 0.012064 0.241483 +0.262166 -0.016175 0.27233 +-0.143576 -0.133746 -0.615782 +-0.160869 -0.119135 -0.696484 +-0.135325 -0.130063 -0.703567 +0.171942 -0.002366 0.335949 +0.190821 0.010089 0.346412 +0.080132 -0.074061 -0.708891 +0.100808 -0.096899 -0.79974 +0.357678 0.048422 -0.139516 +-0.071777 0.023237 0.672706 +0.237792 -0.032614 0.366062 +0.228778 -0.055637 0.359697 +0.2494 -0.035982 0.31464 +0.219264 0.03807 0.176292 +-0.25479 -0.03794 0.26771 +-0.253873 -0.042551 0.241433 +-0.236493 -0.050336 0.260313 +-0.13773 -0.005931 -0.414142 +-0.134495 0.011459 -0.41487 +0.163194 -0.058909 -0.917034 +0.17152 -0.016533 -0.931828 +-0.169841 -0.110147 -0.611986 +-0.178531 -0.092624 -0.689969 +-0.153471 -0.080507 0.409463 +-0.176946 -0.072394 0.374346 +-0.193866 0.011998 0.298053 +-0.17973 -0.005852 0.290346 +-0.200223 0.013169 0.252261 +-0.120376 0.02512 -0.419874 +-0.183095 -0.070451 -0.608863 +-0.182665 -0.061429 -0.687606 +-0.193025 0.006998 0.426495 +-0.146159 -0.047714 -0.430289 +0.050021 0.062413 0.195904 +0.352977 0.0731 -0.151044 +0.138139 0.11513 -0.950869 +0.330384 0.03604 -0.096696 +0.339748 0.038028 -0.111719 +-0.008455 -0.099159 0.047585 +0.134852 -0.023383 -0.900373 +-0.19131 -0.081309 0.407231 +0.33174 0.043482 -0.109801 +0.332326 0.053947 -0.107568 +0.164425 -0.024219 0.330085 +0.157925 -0.017074 0.358447 +0.25467 -0.037979 0.267736 +0.04634 0.016295 0.551532 +0.071798 0.145186 0.383741 +-0.149476 -0.121491 -0.974756 +-0.144727 -0.085602 -0.97833 +-0.112139 -0.135913 -0.616767 +-0.113224 -0.125811 -0.707323 +0.06408 0.142024 0.402264 +0.094829 0.15407 0.402267 +0.235265 -0.053833 0.306932 +0.357062 0.077449 -0.136492 +0.116128 -0.036368 -0.78768 +0.357733 0.052408 -0.149996 +-0.112323 0.042538 -0.375643 +-0.014254 0.018502 0.815081 +-0.026157 -0.082194 0.736953 +0.000167 -0.084481 0.746748 +0.122329 -0.012356 -0.929538 +0.181078 0.111755 -0.961681 +0.348813 0.049767 -0.152316 +-0.098522 0.031918 -0.41636 +-0.097817 0.045961 -0.374305 +-0.17422 -0.079079 -0.788737 +0.169811 -0.096837 -0.930936 +0.066086 0.088146 0.321504 +-0.149399 -0.119766 -0.783049 +-0.186242 -0.062921 0.337147 +-0.16574 -0.102936 -0.790708 +0.000149 0.001448 0.814988 +-0.191474 -0.010617 0.245282 +0.057009 -0.098046 -0.058783 +0.345692 0.040527 -0.133543 +0.261673 0.008906 0.072566 +0.207601 0.034662 -0.964126 +0.069559 -0.002266 0.673485 +0.361242 0.06737 -0.13841 +-0.017244 -0.065856 0.221488 +0.127126 0.125889 0.440487 +-0.218422 0.022526 0.260721 +0.209082 -0.067097 0.348097 +0.176827 -0.072432 0.374373 +0.186123 -0.06296 0.337175 +0.161442 0.11035 -0.974177 +0.137745 0.119463 -0.96321 +0.237267 0.007735 0.314909 +-0.073333 0.00819 0.671301 +0.250165 -0.012465 0.317687 +0.255373 0.003608 0.276391 +0.079771 -0.101541 -0.132485 +0.133483 0.029525 -0.373842 +-0.2092 -0.067057 0.348069 +0.1605 -0.112745 -0.963402 +0.213158 -0.059199 0.297507 +0.191524 -0.05003 0.289964 +0.054443 -0.034838 0.575107 +0.054752 -0.015075 0.563712 +0.119634 -0.051103 -0.913871 +-0.17563 -0.080145 0.523413 +0.146787 -0.021377 0.536589 +0.097999 0.024274 0.525773 +0.094615 0.015938 0.534984 +-0.309169 0.052202 0.035263 +-0.308531 0.029294 0.103042 +-0.298682 0.049946 0.106018 +-0.297572 0.066955 -0.047369 +-0.299564 0.033494 0.150195 +-0.291675 0.007987 0.193759 +-0.277382 0.037895 0.197003 +0.231763 -0.00842 0.365842 +-0.301845 0.003632 0.142656 +-0.277477 0.054656 0.148803 +-0.247437 0.050371 0.189756 +-0.054748 0.033272 0.526506 +0.122437 -0.113899 0.007519 +-0.193282 0.020798 -0.942916 +0.073563 0.142338 0.425029 +0.066203 0.062522 0.091972 +0.000232 0.073015 0.137844 +-0.047791 0.043658 0.604435 +0.212468 0.008589 0.36307 +0.176166 0.106477 -0.95205 +0.178626 -0.029829 0.287186 +-0.079237 0.005393 0.695935 +-0.076123 0.021169 -0.417027 +-0.169275 -0.04711 0.331153 +0.216739 -0.026752 0.424907 +0.000277 -0.068158 0.778329 +0.191192 -0.081347 0.407258 +-0.098086 0.024286 0.525765 +0.350164 0.122984 -0.143516 +0.183996 0.007199 0.388155 +0.212216 -0.004421 0.405766 +-0.184116 0.007238 0.38813 +0.10786 0.133032 0.444293 +-0.315013 0.1033 -0.056114 +0.343636 0.114749 -0.122414 +0.105901 0.15086 0.422464 +0.087645 0.135578 0.442222 +-0.279045 0.060032 0.097165 +0.018609 0.031918 0.534297 +0.109621 0.089465 -0.973165 +0.100191 0.074932 -0.964246 +-0.146831 -0.018057 -0.697226 +0.21641 -0.058012 0.417767 +0.065129 -0.080297 -0.613455 +0.067307 -0.090024 -0.544694 +0.055978 -0.05627 -0.544923 +-0.228896 -0.055598 0.359671 +0.153721 -0.115869 -0.552604 +0.133373 -0.106461 -0.501061 +0.12403 -0.130059 -0.552657 +0.192908 0.007033 0.426625 +0.15375 -0.002896 0.38574 +0.169726 -0.000509 0.366447 +-0.062061 0.009439 0.652188 +-0.056839 0.020863 0.627445 +-0.121258 -0.014342 -0.700516 +-0.214751 -0.048761 0.251994 +-0.212159 -0.041729 0.226274 +-0.197533 -0.033816 0.24581 +0.173921 -0.082221 -0.963161 +0.168202 -0.061975 -0.975816 +0.177887 -0.043544 -0.957618 +-0.095604 -0.024233 -0.703071 +-0.102468 -0.052468 -0.79493 +-0.198954 -0.008168 0.473288 +0.105332 -0.111549 -0.498398 +0.092592 -0.120714 -0.548836 +-0.213277 -0.05916 0.297479 +-0.191642 -0.049991 0.289936 +0.359854 0.065564 -0.149794 +0.14721 0.014138 0.493835 +0.175852 -0.005093 0.511355 +0.181789 -0.036859 -0.610575 +0.170095 -0.017463 -0.554248 +0.17871 -0.046872 -0.552482 +0.079132 -0.094498 -0.497075 +-0.031564 0.064185 0.586557 +-0.068814 -0.002386 0.673389 +0.144645 -0.133741 -0.615754 +-0.169618 -0.057919 -0.788991 +-0.164544 -0.02418 0.330059 +-0.178745 -0.029791 0.287159 +-0.353449 0.100297 -0.144988 +0.170909 -0.110143 -0.611958 +0.173346 -0.081978 -0.551082 +0.144451 0.001541 -0.553988 +0.184164 -0.070446 -0.608835 +0.058245 0.041798 0.617799 +-0.359902 0.069812 -0.126473 +-0.351593 0.071362 -0.14978 +-0.348792 0.075551 -0.122467 +0.052327 0.025048 0.537409 +0.156605 -0.088769 -0.502422 +0.113103 -0.135944 -0.616971 +0.061286 -0.046683 -0.614011 +0.060984 -0.025141 -0.547473 +0.059732 -0.036682 -0.498685 +0.072668 -0.018394 -0.615161 +-0.308402 0.029099 0.014168 +-0.320004 0.067021 -0.007001 +0.06213 -0.067852 -0.496605 +-0.112863 -0.115721 -0.78648 +0.122134 0.107746 -0.974322 +0.084451 -0.117277 -0.615599 +-0.09246 -0.103354 -0.708364 +-0.101836 -0.096328 -0.797447 +0.000687 0.081648 0.798072 +0.015952 0.101963 0.777649 +-0.014695 0.101868 0.777555 +0.017383 0.060739 0.806361 +0.189972 0.015591 -0.975093 +0.191862 -0.004379 -0.960993 +0.000267 -0.024418 0.808928 +-0.184152 -0.052972 0.530994 +0.180228 -0.027037 0.531658 +0.132653 -0.001324 -0.614735 +-0.293724 0.071226 0.020867 +0.164928 0.009787 0.470594 +0.165244 -0.011569 -0.613368 +0.33453 0.113225 -0.100287 +-0.317145 0.038512 -0.015189 +-0.235384 -0.053793 0.306905 +-0.165729 -0.069459 -0.874049 +0.168543 -0.057623 -0.504902 +0.169155 -0.04715 0.33118 +0.000295 -0.048076 0.797157 +0.100339 -0.002801 -0.615402 +-0.24952 -0.035943 0.314613 +-0.344146 0.066069 -0.129231 +-0.353663 0.060882 -0.126628 +0.321049 0.068382 -0.007936 +-0.233519 -0.049564 0.232094 +-0.176089 -0.005058 0.511692 +0.065548 -0.047106 -0.461278 +-0.237912 -0.032574 0.366035 +0.354767 0.102019 -0.146066 +-0.049903 0.064645 0.606883 +0.092275 -0.088632 -0.463041 +-0.215753 0.016943 0.30734 +-0.19094 0.010127 0.346385 +0.347086 0.098279 -0.123603 +-0.250285 -0.012426 0.317661 +-0.23188 -0.008467 0.365694 +0.119313 0.028689 0.512206 +-0.212585 0.008554 0.362939 +-0.212317 -0.0049 0.405009 +0.156511 -0.038022 0.355508 +-0.027271 0.106695 0.664112 +-0.237387 0.007774 0.314882 +0.100543 -0.091974 0.537372 +-0.061758 0.062859 0.633349 +-0.066238 -0.090029 -0.544722 +-0.0584 0.041725 0.617799 +-0.066718 0.02961 0.643738 +-0.081869 -0.044364 -0.703365 +-0.096247 -0.072048 -0.795993 +-0.171555 -0.034578 -0.692527 +0.153797 -0.057313 0.371336 +-0.080103 -0.073706 -0.706604 +-0.216857 -0.026786 0.424776 +-0.14007 -0.023534 -0.900373 +0.125964 0.090476 0.363992 +0.096007 0.076435 0.350796 +0.138416 0.051197 0.39081 +-0.052406 0.099858 0.672279 +-0.058922 0.096015 0.666199 +-0.082735 0.018206 0.705165 +0.088088 -0.111252 0.1119 +0.122322 -0.083917 0.107098 +0.094708 -0.129284 0.049443 +0.111728 -0.112324 0.082435 +0.06644 -0.127597 0.083167 +0.117103 0.023259 0.342647 +0.024221 -0.111692 0.078087 +0.049197 -0.110788 0.111325 +0.020854 -0.089789 0.106589 +-0.039595 0.038126 -0.123114 +0.196143 -0.082017 0.459588 +0.071653 0.049761 0.052075 +0.068917 -0.092263 0.142257 +0.153351 -0.080547 0.40949 +0.162002 -0.095627 0.452908 +0.17152 -0.09458 0.495315 +0.138631 0.086743 0.448996 +-0.262284 -0.016136 0.272303 +0.115985 -0.03189 -0.947976 +-0.240876 0.018577 0.268688 +-0.021682 -0.068079 0.181314 +0.024394 -0.068695 0.1802 +0.103653 -0.086124 -0.928478 +0.118736 -0.11038 -0.897024 +0.023641 -0.047052 0.792444 +0.130924 -0.08206 0.442419 +0.163385 -0.097974 -0.977937 +0.065571 0.074633 0.705359 +0.075525 0.044237 0.706769 +0.155403 0.080724 -0.978019 +-0.200349 -0.031877 0.511405 +-0.180522 -0.027003 0.532176 +0.316374 0.105045 -0.057311 +0.298933 0.0687 -0.048566 +0.101867 0.027765 0.144359 +0.078465 0.050928 0.154908 +-0.255493 0.003647 0.276365 +0.044808 0.051491 0.797208 +0.056032 0.025116 0.792472 +0.057676 0.061247 0.779862 +0.032619 0.076882 0.792434 +0.044295 0.090641 0.76996 +0.153823 -0.023485 0.066935 +0.140312 -0.041279 0.102405 +0.143659 -0.073205 0.071343 +0.044913 -0.130195 0.048916 +0.328643 0.107482 -0.089929 +0.359103 0.120508 -0.142271 +-0.116711 -0.063894 -0.872701 +0.037623 0.015333 0.806564 +0.329793 0.106175 -0.051768 +0.013631 -0.065663 0.215941 +0.018712 0.004167 0.813279 +-0.202019 -0.01911 0.207767 +0.03988 -0.080503 0.145011 +0.11583 0.00439 -0.951472 +0.063954 0.068662 0.757824 +-0.088107 -0.10108 0.514606 +0.304269 0.044884 -0.059759 +0.071426 0.032298 0.769955 +0.33071 0.036433 -0.056124 +-0.059257 0.073308 0.277159 +0.067868 -0.030914 -0.431164 +0.051605 0.094388 0.699984 +0.308124 0.031521 -0.022304 +-0.223946 -0.021616 0.161188 +-0.209652 0.008291 0.164432 +0.11894 0.10209 0.463365 +0.179601 -0.092619 -0.689941 +-0.302907 0.043139 -0.058563 +-0.225476 -0.039879 0.205727 +0.067115 0.072045 0.732783 +0.049534 0.098277 0.740572 +-0.037865 0.106055 0.663632 +0.134121 -0.101168 0.050634 +0.049119 -0.08204 0.316253 +0.075891 -0.017079 0.704095 +0.145693 0.082887 0.387494 +0.148513 0.077463 0.419952 +-0.020141 0.076839 0.079383 +-0.073478 0.071222 -0.125881 +0.18373 -0.061424 -0.687579 +-0.119577 0.075674 -0.127002 +-0.256679 0.051936 0.08732 +-0.252373 0.033449 0.076668 +-0.08359 -0.11721 -0.615163 +-0.155435 0.052239 -0.127829 +0.140036 -0.126156 -0.962029 +0.112933 0.060372 -0.975334 +-0.060424 -0.046616 -0.613575 +-0.071807 -0.018327 -0.614725 +0.126995 -0.004658 0.124934 +0.121818 0.020068 -0.968705 +-0.132384 0.052987 -0.930089 +0.020565 -0.022346 0.80643 +0.172617 -0.034573 -0.6925 +0.075579 -0.022709 -0.438903 +0.067669 -0.009307 -0.416099 +0.14779 -0.018088 -0.697429 +0.127355 -0.021293 -0.963114 +0.081894 -0.04472 -0.705653 +0.095626 -0.024589 -0.705358 +0.121488 -0.014626 -0.70234 +-0.168278 -0.059321 -0.917305 +0.079233 0.03866 0.740287 +-0.261791 0.008945 0.072539 +-0.231986 0.004352 0.117807 +0.21154 -0.046599 0.468037 +0.198718 -0.008203 0.472951 +0.043258 0.071539 0.141414 +-0.127551 -0.012406 -0.929538 +-0.026188 0.058074 0.012186 +0.112256 0.042497 -0.375606 +0.134501 0.011415 -0.414821 +0.026596 -0.068706 0.768445 +0.097698 0.045923 -0.374278 +0.137736 -0.005975 -0.414093 +0.122283 -0.115801 -0.974611 +-0.064269 -0.08023 -0.613019 +0.19953 -0.031896 0.50925 +0.10673 -0.105034 -0.952919 +-0.174945 -0.096887 -0.930936 +-0.166068 -0.090536 -0.885475 +0.137206 0.059278 0.473049 +0.108901 0.069512 0.488713 +0.043453 -0.067073 0.234408 +0.148483 0.0426 0.439737 +-0.121795 0.024085 -0.939902 +0.070013 -0.024905 -0.463256 +0.094521 0.054911 -0.332617 +-0.044988 -0.069707 0.571411 +0.196549 -0.06939 0.502664 +-0.269302 0.012103 0.241456 +0.139593 -0.085551 -0.97833 +-0.1813 0.106426 -0.95205 +0.084087 -0.118334 -0.0167 +0.070692 -0.131623 0.017117 +-0.027246 0.032932 -0.019514 +0.068233 0.043141 -0.330533 +0.169942 -0.022509 -0.294929 +-0.066197 0.062519 0.092052 +-0.246955 0.034265 0.236012 +0.000944 0.079609 0.57354 +0.157193 0.037473 -0.975575 +-0.017007 0.074555 0.577188 +-0.092451 0.046276 0.119469 +-0.101966 0.027771 0.14434 +0.148856 -0.027476 -0.970453 +0.357818 0.09714 -0.107473 +-0.253954 -0.017301 0.119624 +-0.062212 -0.069614 0.179375 +-0.118028 0.064277 0.011804 +-0.165178 0.032598 -0.050201 +-0.078283 0.059413 0.009658 +-0.139111 0.065256 -0.050545 +-0.067102 -0.127608 0.083129 +-0.093764 -0.129297 0.049364 +-0.044257 -0.130188 0.048792 +-0.02388 -0.118223 0.017593 +-0.133511 -0.101327 0.05037 +-0.070802 -0.131593 0.016976 +-0.045983 -0.121947 -0.007695 +-0.12708 -0.004649 0.124927 +-0.122402 -0.083924 0.107013 +0.042901 -0.013411 0.798205 +-0.110434 -0.112461 0.082173 +-0.300537 0.030467 -0.022794 +0.064719 -0.006094 0.777678 +0.06977 -0.03297 0.746087 +0.079517 0.002111 0.744329 +-0.097479 0.07402 -0.050754 +0.04895 -0.041262 0.777678 +0.051476 -0.062134 0.744591 +0.077936 0.061074 -0.282267 +0.049416 0.035999 -0.279656 +0.085 0.027706 -0.418945 +0.093614 0.023107 -0.434949 +0.083504 0.018742 -0.434733 +0.10551 0.024301 -0.434957 +0.099179 0.017905 -0.450637 +0.104225 0.010943 -0.473395 +0.093515 0.007878 -0.466875 +0.085533 -0.001387 -0.479578 +0.086084 0.003223 -0.460441 +0.079785 0.00487 -0.444702 +0.075304 0.009084 -0.435316 +0.069238 0.013881 -0.415505 +0.064922 0.011603 -0.400495 +0.067382 0.022857 -0.391336 +0.071056 0.033647 -0.36988 +0.075886 0.030472 -0.393918 +0.086388 0.036786 -0.396518 +0.087717 0.044492 -0.374683 +0.096522 0.042056 -0.387442 +0.107434 0.038476 -0.397641 +0.11772 0.03446 -0.397719 +0.111318 0.030022 -0.419755 +0.115082 0.022058 -0.434555 +-0.154396 -0.04401 -0.821796 +-0.146033 -0.03405 -0.792788 +-0.14559 -0.038588 -0.836323 +-0.151405 -0.029697 -0.744407 +-0.138817 -0.024076 -0.746508 +-0.133452 -0.013771 -0.699098 +-0.128459 -0.022903 -0.747439 +-0.117281 -0.025556 -0.748756 +-0.126527 -0.031998 -0.793891 +-0.123915 -0.04171 -0.82303 +-0.136441 -0.037708 -0.836495 +-0.144431 -0.036866 -0.866704 +-0.140784 -0.036521 -0.889768 +-0.14861 -0.032492 -0.887839 +-0.148537 -0.019592 -0.89748 +-0.156102 -0.036015 -0.894347 +-0.163448 -0.037764 -0.906158 +-0.161411 -0.050346 -0.895794 +-0.16356 -0.066484 -0.898927 +-0.163188 -0.058661 -0.874199 +-0.167186 -0.064576 -0.838923 +-0.162506 -0.054127 -0.837091 +-0.16378 -0.04718 -0.789485 +-0.17189 -0.048281 -0.739308 +-0.162645 -0.037607 -0.742288 +-0.161111 -0.024278 -0.695175 +0.034314 0.049359 0.593313 +0.031715 0.064242 0.586614 +0.039882 0.05637 0.594047 +0.018033 0.074655 0.577287 +0.027023 0.077375 0.582822 +0.035109 0.075342 0.589601 +0.043328 0.076991 0.600573 +0.042798 0.064854 0.596433 +0.049759 0.052827 0.603861 +0.055703 0.052902 0.614078 +0.06172 0.031236 0.625212 +0.054571 0.031488 0.61617 +0.04518 0.02485 0.613831 +0.042287 0.036067 0.605219 +0.025777 0.044468 0.593641 +0.014607 0.039923 0.594763 +0.008979 0.05128 0.586359 +-0.007541 0.051232 0.586311 +0.00073 0.061307 0.579418 +-0.004866 0.069214 0.575659 +0.006351 0.069261 0.575706 +0.009533 0.077746 0.574115 +0.137823 -0.072293 -0.448672 +0.14702 -0.076721 -0.464719 +0.144444 -0.066039 -0.45078 +0.147212 -0.087648 -0.480882 +0.156045 -0.07636 -0.482139 +0.164822 -0.073968 -0.503427 +0.161374 -0.062994 -0.483552 +0.163648 -0.047787 -0.484884 +0.157219 -0.052742 -0.467207 +0.150737 -0.045026 -0.452725 +0.149314 -0.055426 -0.451855 +0.142063 -0.058432 -0.430772 +0.141529 -0.051298 -0.399741 +0.13082 -0.059998 -0.402149 +0.121509 -0.065181 -0.367875 +0.121133 -0.064061 -0.400636 +0.10807 -0.065477 -0.398856 +0.125755 -0.068748 -0.427151 +0.129082 -0.076721 -0.446488 +0.117464 -0.078584 -0.445528 +0.127782 -0.087858 -0.46154 +0.123989 -0.099707 -0.478668 +0.136715 -0.094357 -0.479887 +0.146434 -0.100096 -0.501818 +0.0751 -0.005894 -0.439339 +0.068813 0.003522 -0.420396 +0.060073 0.00326 -0.38662 +0.058085 -0.009248 -0.386583 +0.052855 0.004846 -0.358571 +0.045201 0.005832 -0.332608 +0.053793 0.018972 -0.343158 +0.056407 0.030966 -0.330399 +0.064471 0.033784 -0.344822 +0.07639 0.0447 -0.34469 +0.072056 0.038791 -0.352654 +0.079377 0.042965 -0.360043 +-0.322854 0.106248 -0.077181 +-0.313158 0.092123 -0.066962 +-0.320502 0.095611 -0.078823 +-0.333369 0.102499 -0.098747 +-0.346526 0.104262 -0.113642 +-0.337765 0.107296 -0.110064 +-0.338259 0.116265 -0.110842 +0.017746 0.040951 0.812066 +0.009216 0.030475 0.814915 +0.020663 0.028746 0.813476 +-0.00773 0.030427 0.814869 +0.000519 0.020385 0.816414 +-0.005597 0.011172 0.816161 +0.006185 0.011219 0.816208 +0.009217 -0.000749 0.81448 +0.015179 0.018604 0.815181 +0.027084 0.022222 0.812192 +0.028174 0.010026 0.810997 +0.035581 0.02961 0.807508 +0.044336 0.035043 0.802024 +0.037243 0.045818 0.804088 +0.03363 0.059166 0.801916 +0.025828 0.050636 0.807364 +0.009273 0.050657 0.810793 +0.000687 0.061143 0.808539 +-0.007787 0.05061 0.810747 +-0.024567 0.050517 0.807247 +-0.016261 0.040856 0.811973 +-0.019401 0.028627 0.813359 +0.18904 0.05325 -0.939661 +0.182748 0.035737 -0.933876 +0.195966 0.039643 -0.94641 +0.164345 0.034493 -0.924958 +0.174357 0.016942 -0.928091 +0.167372 -0.001747 -0.921102 +0.179159 0.00241 -0.937492 +0.183653 -0.011515 -0.948821 +0.192301 0.007064 -0.952195 +0.200541 0.015069 -0.963765 +0.200717 0.0255 -0.955328 +0.206609 0.045938 -0.955087 +0.212793 0.056229 -0.96256 +0.209337 0.06592 -0.955087 +0.201419 0.088055 -0.954943 +0.201847 0.076324 -0.949784 +0.193309 0.085722 -0.948339 +0.192192 0.06995 -0.943036 +0.174661 0.068452 -0.937492 +0.162798 0.083759 -0.940143 +0.150956 0.069695 -0.933635 +0.12904 0.072825 -0.934358 +0.146932 0.05325 -0.926886 +0.14579 0.034745 -0.918932 +-0.022539 -0.100064 0.091452 +-0.034476 -0.101114 0.108583 +-0.037401 -0.114645 0.094311 +-0.030398 -0.087436 0.126216 +-0.046951 -0.094812 0.127269 +-0.056899 -0.086745 0.145514 +-0.0631 -0.10197 0.128565 +-0.079022 -0.103607 0.1285 +-0.070143 -0.11564 0.112648 +-0.079179 -0.122321 0.098194 +-0.056918 -0.122012 0.097079 +-0.043539 -0.124662 0.081103 +-0.055252 -0.131201 0.065892 +-0.03184 -0.122629 0.064184 +-0.021241 -0.119156 0.047819 +-0.014697 -0.106949 0.062432 +-0.005273 -0.087201 0.062205 +-0.011532 -0.094401 0.076493 +-0.011787 -0.086838 0.089827 +-0.004127 -0.078303 0.089556 +-0.007918 -0.081657 0.10567 +0.000538 -0.082118 0.117488 +-0.014592 -0.081206 0.124929 +-0.022503 -0.076678 0.144759 +0.131016 0.030007 -0.921102 +0.143786 0.015279 -0.910738 +0.14456 -0.003872 -0.904471 +0.155358 -0.003662 -0.909773 +0.1614 -0.021081 -0.914594 +0.119759 0.090982 -0.939902 +0.114652 0.074994 -0.938938 +0.10756 0.073056 -0.946893 +0.113518 0.057939 -0.939902 +0.111983 0.043341 -0.944241 +0.119362 0.03847 -0.932912 +0.123592 0.019284 -0.927128 +0.122701 0.016798 -0.434688 +0.119816 0.015003 -0.450125 +0.123458 0.007119 -0.467249 +0.115291 0.010284 -0.473285 +0.110572 0.006901 -0.501334 +0.123614 0.037812 -0.375614 +0.126764 0.02868 -0.397726 +0.136835 0.018556 -0.394055 +0.127983 0.018983 -0.419896 +0.128589 0.010047 -0.432236 +0.13734 0.003116 -0.416256 +0.130069 -6.1e-05 -0.434212 +0.133967 -0.011808 -0.420916 +0.128438 -0.005142 -0.442843 +0.130463 -0.011981 -0.449293 +0.127148 -0.001314 -0.460421 +0.131875 0.000305 -0.482078 +0.07376 -0.060361 -0.446447 +0.081422 -0.070317 -0.446155 +0.074366 -0.059211 -0.42425 +0.083033 -0.080999 -0.460094 +0.092136 -0.079874 -0.450203 +0.101872 -0.09105 -0.460544 +0.104184 -0.079577 -0.445754 +0.101264 -0.071654 -0.425042 +0.095197 -0.066519 -0.397707 +0.081907 -0.063519 -0.3956 +0.065807 -0.059207 -0.358372 +0.070643 -0.057006 -0.394146 +0.063182 -0.044838 -0.390651 +0.049602 -0.032254 -0.350668 +0.058213 -0.033789 -0.389715 +0.060478 -0.025451 -0.402212 +0.064594 -0.038002 -0.423824 +0.068956 -0.050201 -0.446956 +0.067741 -0.059835 -0.460462 +0.063104 -0.056832 -0.477018 +0.067905 -0.071221 -0.476469 +0.068524 -0.082087 -0.496373 +0.075535 -0.082429 -0.476394 +0.086245 -0.092693 -0.476471 +-0.13985 -0.032976 0.544864 +-0.121856 -0.015987 0.544631 +-0.157599 -0.047413 0.542014 +-0.138462 -0.044574 0.546762 +-0.118631 -0.049816 0.550605 +-0.118026 -0.037195 0.550138 +-0.093064 -0.030237 0.555935 +-0.109946 -0.022646 0.549189 +-0.095244 -0.014451 0.549912 +-0.07089 -0.019631 0.558345 +-0.070176 -0.006256 0.552081 +-0.052827 -0.000471 0.559068 +-0.061982 0.00917 0.546538 +-0.052401 0.02565 0.53756 +-0.071502 0.020137 0.538825 +-0.097721 0.006941 0.540286 +-0.115382 0.000856 0.538372 +-0.119433 0.007059 0.530845 +-0.111344 0.022729 0.520377 +-0.132721 0.00573 0.523074 +-0.1349 0.020916 0.511005 +-0.151237 -0.002315 0.520796 +-0.162466 0.000964 0.509945 +-0.165064 -0.01331 0.526696 +-0.178461 -0.01867 0.527382 +-0.171972 -0.028404 0.534845 +-0.183187 -0.037055 0.533602 +-0.17008 -0.044257 0.538687 +-0.169844 -0.059164 0.536704 +-0.011435 -0.071785 0.162323 +-0.033151 -0.071885 0.162021 +-0.043355 -0.069196 0.184369 +-0.050995 -0.074575 0.161725 +-0.067752 -0.080186 0.156535 +-0.079645 -0.075422 0.161214 +-0.081894 -0.09013 0.145433 +-0.096184 -0.097408 0.127305 +0.155193 -0.042267 -0.328511 +0.144044 -0.056138 -0.331802 +0.146565 -0.046701 -0.362179 +0.14187 -0.067816 -0.300288 +0.129697 -0.065918 -0.33509 +0.112584 -0.069761 -0.334921 +0.145724 -0.04062 -0.397722 +0.14768 -0.037354 -0.429624 +0.14385 -0.031198 -0.410293 +0.13465 -0.019295 -0.431087 +0.139795 -0.022598 -0.40794 +0.144662 -0.015466 -0.390082 +0.147078 -0.005504 -0.387421 +0.156843 -0.015484 -0.353528 +0.16598 -0.010938 -0.323817 +0.162478 -0.027604 -0.325149 +0.165205 -0.038736 -0.295989 +0.17237 -0.033081 -0.260025 +0.16446 -0.050074 -0.260846 +0.161644 -0.06222 -0.2196 +0.152275 -0.065284 -0.261854 +0.137313 -0.078934 -0.26276 +-0.035914 0.029887 0.535065 +-0.035627 0.023916 0.550875 +-0.045107 -0.011677 0.572079 +-0.044876 0.004108 0.5716 +-0.044648 0.008032 0.590438 +-0.040072 0.016353 0.569512 +-0.037008 0.012413 0.596277 +-0.030016 0.020549 0.569024 +-0.022786 0.024164 0.555526 +-0.027801 0.026911 0.545729 +-0.027459 0.037248 0.523334 +-0.15181 -0.045272 0.070061 +-0.143513 -0.057344 0.089619 +-0.147568 -0.033066 0.086242 +-0.133135 -0.081212 0.091882 +-0.134142 -0.064362 0.105115 +-0.121507 -0.066958 0.122248 +-0.130141 -0.046087 0.118848 +-0.125061 -0.026671 0.131923 +-0.135215 -0.024117 0.114596 +-0.135435 -0.002784 0.106996 +-0.143273 -0.018113 0.097486 +-0.150184 -0.01141 0.074829 +-0.152458 0.008475 0.053013 +-0.157561 -0.009098 0.049444 +-0.166125 -0.015147 0.017548 +-0.15867 -0.030222 0.04676 +-0.154641 -0.056404 0.046788 +-0.159489 -0.064526 0.015852 +-0.149614 -0.077279 0.04327 +-0.14235 -0.093183 0.038038 +-0.138852 -0.091657 0.060299 +-0.123816 -0.106646 0.066798 +-0.129778 -0.095184 0.080177 +-0.118404 -0.100227 0.094179 +-0.170441 -0.04876 -0.016736 +-0.162724 -0.069586 -0.017523 +-0.163057 -0.075771 -0.054285 +-0.151345 -0.088426 -0.018273 +-0.136028 -0.10162 -0.018561 +-0.140846 -0.09927 0.00987 +-0.129984 -0.108136 0.032549 +-0.170585 -0.002107 -0.013763 +-0.173047 -0.025564 -0.015013 +-0.178563 -0.035749 -0.052958 +-0.181919 -0.023271 -0.091209 +-0.179103 -0.044763 -0.092663 +-0.17597 -0.052477 -0.134767 +-0.171315 -0.06396 -0.093206 +-0.1577 -0.080727 -0.092798 +0.131039 0.13736 0.411877 +0.140198 0.119042 0.411468 +0.135377 0.131816 0.400307 +0.144295 0.099758 0.424146 +0.146946 0.09759 0.408961 +0.148633 0.077584 0.402356 +0.146465 0.09759 0.394257 +0.141644 0.100482 0.37859 +0.139715 0.118559 0.38881 +0.130556 0.136395 0.392112 +0.125229 0.136629 0.384559 +0.123446 0.146097 0.401633 +0.113217 0.154187 0.409708 +0.122121 0.147965 0.414047 +0.127664 0.139047 0.421037 +0.123473 0.137444 0.430974 +0.132243 0.124585 0.432438 +0.132484 0.110605 0.445597 +0.139715 0.103616 0.435956 +0.1455 0.079994 0.435619 +0.105464 -0.079538 -0.965452 +0.108877 -0.059028 -0.964005 +0.113441 -0.073235 -0.973405 +0.112164 -0.048839 -0.948579 +0.114524 -0.041362 -0.959667 +0.118654 -0.025019 -0.956534 +0.124001 -0.034779 -0.963282 +0.13177 -0.046317 -0.96979 +0.14364 -0.059065 -0.97437 +0.126855 -0.067107 -0.975575 +0.120626 -0.088659 -0.978708 +0.12822 -0.105502 -0.979672 +0.115311 -0.104016 -0.975093 +0.110603 -0.112693 -0.967861 +0.106386 -0.09863 -0.966175 +0.102106 -0.0885 -0.953641 +0.104124 -0.095288 -0.938938 +0.103842 -0.077671 -0.938697 +0.107859 -0.071404 -0.923031 +0.110488 -0.058521 -0.935082 +0.11693 -0.040685 -0.934599 +-0.074463 -0.077453 0.307102 +-0.078401 -0.059789 0.264934 +-0.060105 -0.068898 0.271716 +-0.094806 -0.048793 0.261802 +-0.092646 -0.040849 0.223745 +-0.098855 -0.019207 0.193097 +-0.093596 -0.035382 0.193387 +-0.097932 -0.038457 0.172604 +-0.082951 -0.049343 0.190749 +-0.078645 -0.064003 0.179784 +-0.067216 -0.061508 0.206435 +-0.051503 -0.065799 0.210524 +-0.056095 -0.066328 0.240663 +-0.042784 -0.069414 0.257042 +-0.030736 -0.06768 0.230518 +-0.025676 -0.071542 0.257965 +-0.009122 -0.069386 0.246128 +-0.02034 -0.08079 0.311141 +-0.015722 -0.091002 0.38749 +-0.038388 -0.094059 0.388783 +-0.063779 -0.102674 0.454682 +-0.069124 -0.099458 0.402922 +-0.099713 -0.101902 0.417736 +-0.082542 -0.092616 0.359743 +-0.092785 -0.07983 0.333374 +-0.110242 -0.06956 0.339931 +-0.102354 -0.060438 0.304457 +-0.103764 -0.036518 0.268755 +-0.167933 0.08371 -0.940143 +-0.179796 0.068401 -0.937492 +-0.156091 0.069644 -0.933635 +-0.197328 0.069899 -0.943036 +-0.194175 0.053199 -0.939661 +-0.201101 0.039592 -0.94641 +-0.188074 0.035687 -0.933876 +-0.179874 0.016891 -0.928091 +-0.169862 0.034443 -0.924958 +-0.151499 0.034695 -0.918932 +-0.152259 0.053199 -0.926886 +-0.134175 0.072774 -0.934358 +-0.119787 0.074944 -0.938938 +-0.124894 0.090931 -0.939902 +-0.123458 0.104295 -0.947616 +-0.140525 0.105246 -0.944 +-0.164355 0.112976 -0.95099 +-0.161647 0.101154 -0.944964 +-0.1825 0.094357 -0.947134 +-0.198444 0.085672 -0.948339 +-0.206982 0.076274 -0.949784 +-0.26955 -0.027566 0.148414 +-0.269784 -0.031868 0.174756 +-0.283577 -0.021435 0.15516 +-0.254123 -0.038395 0.194776 +-0.268151 -0.032264 0.201521 +-0.266811 -0.032233 0.222687 +-0.27862 -0.020605 0.208538 +-0.283485 -0.005696 0.214455 +-0.290721 -0.008551 0.188788 +-0.298912 0.005133 0.168092 +-0.294046 -0.009776 0.162176 +-0.295288 -0.009656 0.136587 +-0.302695 0.003478 0.118243 +-0.294271 -0.005903 0.112597 +-0.294476 0.000933 0.081539 +-0.281762 -0.011371 0.106581 +-0.268831 -0.011324 0.101957 +-0.268751 -0.021254 0.123824 +-0.253482 -0.027507 0.142668 +-0.238511 -0.021268 0.139045 +-0.237648 -0.031749 0.163265 +-0.223083 -0.032097 0.185407 +-0.238055 -0.038335 0.18903 +-0.240273 -0.043833 0.209926 +-0.092235 -0.053103 0.557851 +-0.065838 -0.057596 0.565579 +-0.068238 -0.048313 0.5663 +-0.06415 -0.06796 0.563892 +-0.052196 -0.056957 0.57459 +-0.039091 -0.057596 0.588712 +-0.044625 -0.051689 0.58389 +-0.046306 -0.037344 0.587499 +-0.054252 -0.04554 0.57714 +-0.069674 -0.038068 0.56485 +-0.070156 -0.028909 0.562199 +-0.110436 -0.062109 0.551569 +-0.113088 -0.075607 0.547471 +-0.091271 -0.07118 0.55544 +-0.061976 -0.078684 0.556998 +-0.307034 0.016774 0.123519 +-0.308559 0.021113 0.09218 +-0.309478 0.03837 0.071997 +-0.310004 0.030432 0.052442 +-0.31328 0.040791 0.018598 +-0.304347 0.019634 0.047448 +-0.29708 0.01208 0.043322 +-0.301366 0.02206 0.010747 +-0.286625 0.011456 0.039936 +-0.278617 0.015978 0.038748 +-0.271092 0.000358 0.073531 +-0.255479 -0.00576 0.098726 +-0.10819 0.051484 0.346852 +-0.091984 0.065889 0.328944 +-0.101225 0.039468 0.317848 +-0.099468 0.030274 0.292639 +-0.11085 0.013589 0.310555 +-0.119912 -0.008526 0.319179 +-0.125871 0.000417 0.343807 +-0.129094 -0.019956 0.347048 +-0.14803 0.06051 0.427228 +-0.148512 0.060269 0.40915 +-0.148753 0.077624 0.402329 +-0.144415 0.068705 0.390109 +-0.139835 0.083407 0.375333 +-0.131157 0.07232 0.373959 +-0.113804 0.08148 0.361907 +-0.165551 0.020785 -0.013373 +-0.173778 0.01109 -0.05048 +-0.172771 0.023123 -0.088693 +-0.179595 0.000111 -0.089983 +-0.182354 -0.010619 -0.132109 +-0.255119 -0.04115 0.231815 +-0.262239 -0.034719 0.239786 +-0.256245 -0.039171 0.250488 +-0.263742 -0.029584 0.251808 +-0.261051 -0.028069 0.269964 +-0.267756 -0.016443 0.253074 +-0.267568 -0.003742 0.255398 +-0.275969 -0.003485 0.237115 +-0.282221 0.010851 0.218632 +-0.229679 -0.046571 0.221176 +-0.24261 -0.046618 0.225799 +-0.245496 -0.048147 0.236884 +-0.347738 0.040042 -0.142918 +-0.353667 0.042125 -0.127058 +-0.356317 0.046678 -0.138321 +-0.35461 0.040786 -0.15294 +0.005749 0.069597 0.154621 +-0.016153 0.071343 0.154745 +-0.022133 0.07466 0.135914 +-0.030434 0.070256 0.160134 +-0.048125 0.065048 0.164629 +-0.033519 0.068932 0.193502 +-0.035154 0.071296 0.236562 +-0.016637 0.072116 0.217562 +0.001976 0.076208 0.235974 +-0.001783 0.071803 0.195631 +0.007337 0.068129 0.165708 +0.029494 0.021295 0.609461 +0.015004 0.021967 0.592485 +0.015432 0.027967 0.60197 +0.000534 0.033823 0.598879 +0.022632 0.119945 0.709601 +0.01864 0.122279 0.72591 +0.030456 0.116728 0.725309 +0.007049 0.124805 0.725863 +0.014014 0.119953 0.745819 +0.007809 0.113608 0.764353 +0.022299 0.110654 0.76278 +0.030941 0.098067 0.775004 +0.035492 0.104674 0.76056 +0.047384 0.096111 0.756079 +0.039022 0.10842 0.743 +0.041374 0.10915 0.725558 +0.051577 0.098428 0.724437 +0.043447 0.106507 0.711471 +0.043852 0.103614 0.701769 +0.035145 0.109797 0.700616 +0.026544 0.112935 0.696855 +0.016846 0.119048 0.696408 +0.003831 0.121368 0.693726 +0.001843 0.12455 0.707691 +-0.005359 0.124757 0.725816 +-0.047895 -0.033112 0.632439 +-0.039997 -0.050565 0.611532 +-0.039458 -0.048356 0.630012 +-0.033538 -0.067356 0.584232 +-0.041964 -0.080732 0.563843 +-0.027989 -0.076875 0.581195 +-0.014495 -0.083384 0.576854 +-0.031229 -0.063274 0.607769 +-0.035367 -0.055128 0.641799 +-0.027961 -0.062423 0.652942 +-0.044647 -0.052561 0.666889 +-0.050951 -0.056102 0.687794 +-0.06074 -0.040712 0.687247 +-0.070191 -0.034184 0.705635 +-0.068844 -0.025178 0.6872 +-0.071198 -0.011523 0.684393 +-0.066181 -0.011895 0.672771 +-0.064748 0.000376 0.663475 +-0.05987 -0.005735 0.657821 +-0.057325 0.002532 0.645455 +-0.054149 -0.014155 0.644219 +-0.049165 -0.019113 0.623133 +-0.047295 -0.006621 0.608268 +-0.046321 -0.021741 0.599809 +-0.045823 -0.025293 0.583643 +0.034705 -0.078761 0.707976 +0.038589 -0.07787 0.725791 +0.025665 -0.084619 0.720957 +0.049905 -0.068353 0.725275 +0.040027 -0.073785 0.742972 +0.04014 -0.066351 0.760543 +0.027533 -0.077494 0.753043 +0.013803 -0.079631 0.757756 +0.013991 -0.085154 0.741814 +0.000149 -0.088699 0.731704 +0.013562 -0.088193 0.726908 +0.012945 -0.087998 0.713803 +0.000278 -0.088283 0.705279 +0.014016 -0.084427 0.698771 +0.017632 -0.076473 0.686237 +0.02777 -0.078283 0.694074 +0.04279 -0.069767 0.694988 +0.052085 -0.05596 0.687934 +0.053071 -0.063014 0.706322 +0.060455 -0.056201 0.725898 +-0.124358 0.015972 0.116484 +-0.130583 0.019448 0.097922 +-0.122774 0.038074 0.084373 +-0.134711 0.030922 0.074441 +-0.136144 0.044633 0.047276 +-0.145615 0.026361 0.05254 +-0.155987 0.025318 0.021507 +-0.344947 0.098323 -0.101601 +-0.354748 0.089292 -0.091377 +-0.351936 0.084312 -0.081462 +-0.345374 0.094633 -0.072209 +-0.352176 0.096955 -0.090405 +-0.353859 0.107433 -0.10818 +-0.330378 0.041738 -0.108605 +-0.330965 0.052203 -0.106372 +-0.321655 0.064517 -0.089461 +-0.31604 0.055047 -0.082705 +-0.306991 0.054927 -0.068061 +-0.311581 0.043207 -0.073344 +-0.309078 0.0326 -0.057447 +-0.320561 0.033007 -0.078988 +-0.339723 0.035357 -0.097872 +-0.338386 0.036283 -0.110522 +-0.343031 0.037898 -0.120054 +-0.340279 0.050512 -0.128731 +-0.335034 0.046233 -0.119727 +0.177365 0.094407 -0.947134 +0.156512 0.101205 -0.944964 +0.159221 0.113026 -0.95099 +0.13539 0.105296 -0.944 +0.118323 0.104346 -0.947616 +0.119172 0.111799 -0.956052 +0.107758 0.098325 -0.956293 +0.100776 0.089941 -0.964247 +0.102101 0.083022 -0.956534 +0.102727 0.067415 -0.955328 +0.204447 0.088714 -0.970754 +0.211592 0.068093 -0.96979 +0.199394 0.079435 -0.975334 +0.210757 0.047042 -0.970995 +0.204398 0.026319 -0.97196 +0.198123 0.038895 -0.975816 +0.178143 0.02451 -0.975575 +0.183176 0.049987 -0.977263 +0.158815 0.059781 -0.977745 +0.183612 0.069476 -0.977986 +0.181941 0.088311 -0.978226 +0.159791 0.099559 -0.977263 +0.178263 0.103536 -0.976057 +0.172326 0.114202 -0.9722 +0.19131 0.103879 -0.971959 +0.20086 0.096689 -0.964969 +0.005181 -0.087214 0.062214 +0.000179 -0.071911 0.07624 +-5.2e-05 -0.073248 0.062226 +0.000429 -0.076374 0.04766 +-0.004377 -0.088818 0.033195 +-4.6e-05 -0.066889 0.02081 +-4e-05 -0.073279 0.033488 +0.004541 -0.088818 0.033436 +0.010327 -0.099053 0.017474 +0.014908 -0.11014 0.033623 +0.033467 -0.126531 0.033382 +0.021891 -0.119156 0.04806 +0.032008 -0.122629 0.064184 +0.014856 -0.106955 0.062437 +0.011436 -0.094421 0.076506 +0.021981 -0.100084 0.091465 +0.012149 -0.086864 0.089845 +0.00887 -0.081677 0.105684 +0.004955 -0.078317 0.089565 +0.105559 0.049912 -0.953401 +0.110176 0.031911 -0.950026 +0.101499 0.059253 -0.963042 +0.100973 0.06804 -0.970754 +0.104367 0.051356 -0.969549 +0.117059 0.042578 -0.973164 +0.110071 0.033648 -0.965693 +0.116072 0.013433 -0.960631 +0.111821 0.023545 -0.95581 +0.114597 0.013327 -0.946169 +0.117434 -0.004796 -0.942795 +0.120635 0.005571 -0.934599 +0.126374 0.000526 -0.92062 +0.115697 0.052927 0.064604 +0.12412 0.057123 0.041569 +0.107076 0.06003 0.039268 +0.135992 0.0446 0.047299 +0.135003 0.05898 0.0145 +0.145382 0.056535 -0.015276 +0.128658 0.066975 -0.016655 +0.119967 0.074178 -0.051056 +0.108637 0.070634 -0.018096 +0.085949 0.067766 -0.01774 +0.097938 0.065546 0.010758 +0.089167 0.05798 0.035697 +0.071956 0.050993 0.033388 +0.082586 0.052298 0.053811 +0.080983 0.052559 0.072149 +0.094446 0.048808 0.075378 +0.10232 0.045278 0.09406 +0.109649 0.047386 0.082601 +0.122678 0.038054 0.084387 +0.130502 0.019442 0.097927 +0.134594 0.030903 0.074455 +0.14572 0.026341 0.052554 +0.156332 0.025299 0.02152 +0.152839 0.008468 0.053018 +0.124276 0.015966 0.116488 +0.135361 -0.002784 0.106996 +0.135135 -0.024123 0.114601 +0.143199 -0.018113 0.097486 +0.147489 -0.03303 0.086306 +0.150351 -0.01141 0.074829 +0.158212 -0.009098 0.049444 +0.159315 -0.030186 0.046824 +0.167008 -0.015154 0.017552 +0.173674 -0.025584 -0.015001 +0.171212 -0.002127 -0.013749 +0.173907 0.011057 -0.050457 +0.165909 0.020759 -0.013355 +0.156984 0.041113 -0.013512 +0.154395 0.051967 -0.049846 +-0.124767 -0.083039 0.539078 +-0.094399 -0.086967 0.543967 +-0.074263 -0.095434 0.538294 +-0.063175 -0.088685 0.5489 +-0.181475 -0.068678 0.527955 +-0.167436 -0.07399 0.532045 +-0.164075 -0.086491 0.522695 +-0.147763 -0.082629 0.53208 +-0.124752 -0.090866 0.530217 +-0.114596 -0.099145 0.514863 +-0.094865 -0.097686 0.529563 +-0.062976 -0.100331 0.523284 +-0.148871 -0.086093 -0.13452 +-0.140218 -0.093475 -0.092171 +-0.118388 -0.101384 -0.091998 +-0.130259 -0.101712 -0.053949 +-0.119931 -0.109546 -0.023769 +0.08764 -0.093707 -0.216992 +0.081115 -0.082263 -0.256671 +0.100588 -0.085991 -0.260314 +0.063332 -0.074299 -0.253294 +0.072724 -0.072541 -0.293332 +0.067366 -0.063606 -0.326429 +0.079865 -0.068112 -0.330731 +0.091596 -0.066713 -0.364781 +0.095734 -0.069876 -0.333607 +0.108066 -0.078032 -0.299852 +0.120622 -0.085307 -0.262494 +0.130847 -0.089998 -0.220525 +0.139655 -0.088782 -0.177295 +0.118948 -0.097977 -0.176964 +0.105677 -0.103228 -0.133752 +0.095775 -0.101002 -0.17561 +0.073108 -0.096493 -0.173495 +0.058404 -0.094484 -0.130672 +0.055271 -0.085402 -0.17082 +0.04176 -0.069767 -0.168502 +0.052019 -0.074652 -0.210941 +0.049896 -0.062168 -0.250626 +0.000105 -0.093426 0.416664 +0.016088 -0.091022 0.387503 +0.014568 -0.097048 0.458884 +0.020875 -0.080817 0.311158 +0.038653 -0.094125 0.389 +0.06913 -0.099564 0.403338 +0.063146 -0.102773 0.454611 +0.092277 -0.103655 0.496372 +0.061235 -0.104008 0.501116 +0.062183 -0.100099 0.522373 +0.035222 -0.101386 0.506885 +0.010127 -0.099013 0.5109 +-0.001957 -0.093507 0.542871 +-0.009467 -0.099126 0.510991 +-0.035298 -0.101612 0.507551 +-0.01426 -0.097154 0.458971 +-0.004296 -0.064566 0.210009 +0.009517 -0.069399 0.246137 +0.026069 -0.071576 0.257988 +0.174655 0.01451 -0.214808 +0.173821 0.004595 -0.257441 +0.168303 0.024016 -0.256211 +0.175462 -0.015423 -0.258951 +0.1718 -0.00441 -0.293582 +0.163634 0.005286 -0.322165 +0.152804 0.016459 -0.33883 +0.157001 0.021904 -0.321052 +0.147233 0.035817 -0.319925 +0.159648 0.031627 -0.291239 +0.159558 0.041268 -0.255456 +0.146861 0.054797 -0.254858 +0.155792 0.05037 -0.212704 +0.148643 0.058914 -0.169526 +0.162318 0.04321 -0.170186 +0.16849 0.034677 -0.128877 +0.172439 0.024603 -0.171537 +0.179366 0.002286 -0.173456 +0.182242 -0.010652 -0.132087 +0.181041 -0.019273 -0.175061 +0.177474 -0.03964 -0.176264 +0.177971 -0.027068 -0.217924 +0.134343 0.058148 -0.289554 +0.134626 0.04881 -0.318837 +0.119812 0.057273 -0.317494 +0.133323 0.040997 -0.33629 +0.109918 0.05439 -0.334077 +0.10214 0.060962 -0.315465 +0.086957 0.059268 -0.313516 +0.095394 0.067287 -0.28474 +0.085052 0.069002 -0.247709 +0.107071 0.071003 -0.250465 +0.119164 0.072706 -0.21002 +0.128348 0.065555 -0.253251 +0.028509 -0.062365 0.653021 +0.036108 -0.055071 0.641878 +0.04558 -0.05245 0.66701 +0.032152 -0.063137 0.60769 +0.040205 -0.048321 0.630067 +0.0408 -0.050443 0.611472 +0.048401 -0.033136 0.632478 +0.049234 -0.019221 0.623613 +0.054612 -0.014209 0.644259 +0.057802 0.002485 0.64549 +0.060721 -0.005676 0.65792 +0.065591 0.000503 0.663586 +0.067219 -0.011753 0.672911 +0.072041 -0.011396 0.684503 +0.069883 -0.025037 0.68734 +0.071229 -0.034042 0.705776 +0.061825 -0.04057 0.687388 +0.007749 -0.071412 0.681176 +0.017824 -0.070295 0.667247 +0.012474 -0.06725 0.654764 +0.000418 -0.068214 0.64512 +-0.011874 -0.069195 0.644369 +0.003307 -0.075928 0.612096 +0.003083 -0.085682 0.577241 +0.018014 -0.079302 0.588959 +0.029115 -0.076531 0.580916 +0.041843 0.031531 -0.054264 +0.046956 0.047724 -0.084097 +0.035804 0.02614 -0.082837 +0.06273 0.065099 -0.084751 +0.052552 0.058425 -0.123083 +0.06228 0.064521 -0.163763 +0.045416 0.048726 -0.162482 +0.040365 0.03884 -0.202978 +0.032626 0.029131 -0.160851 +0.024746 0.009647 -0.160336 +0.028611 0.017247 -0.120191 +0.028682 0.006079 -0.082532 +0.024102 -0.012962 -0.083182 +0.028796 -0.00423 -0.056545 +0.028981 -0.018724 -0.042869 +0.032428 -0.000686 -0.037014 +0.038354 0.015013 -0.034427 +0.044616 0.030651 -0.02668 +0.042996 0.03104 -0.012194 +0.053191 0.043575 -0.011379 +0.061717 0.050002 0.009207 +0.066937 0.059209 -0.016982 +0.073686 0.068823 -0.050037 +0.084616 0.075252 -0.085947 +-0.206772 0.026449 0.219402 +-0.207281 0.026054 0.196015 +-0.217749 0.037713 0.203031 +-0.202416 0.011146 0.190098 +-0.210607 0.024829 0.169402 +-0.217843 0.021974 0.143736 +-0.222708 0.036883 0.149652 +-0.235334 0.046607 0.132096 +-0.233176 0.048541 0.156668 +-0.247204 0.054673 0.163415 +-0.231543 0.048146 0.183434 +-0.231777 0.043844 0.209776 +-0.247845 0.043785 0.215521 +-0.232266 0.037564 0.231569 +-0.231348 0.029005 0.24833 +-0.219678 0.028227 0.243814 +-0.208003 0.020542 0.256459 +-0.208212 0.023241 0.238393 +-0.200402 0.013902 0.233699 +-0.193367 0.002578 0.248041 +-0.195636 0.000164 0.228781 +-0.195384 -0.01338 0.226177 +-0.197141 -0.002198 0.208566 +-0.203681 -0.0054 0.185921 +-0.124502 -0.087365 0.436587 +-0.11767 -0.024869 0.314524 +-0.110453 -0.028499 0.285352 +-0.115015 -0.048167 0.327124 +0.13688 -0.018398 -0.453705 +0.145146 -0.023604 -0.458171 +0.14324 -0.013696 -0.469193 +0.152096 -0.012738 -0.486574 +0.141551 -0.005659 -0.486438 +0.139321 1.7e-05 -0.507051 +-0.107862 0.067365 -0.955328 +-0.110694 0.049862 -0.953401 +-0.106635 0.059203 -0.963042 +-0.117119 0.043291 -0.944241 +-0.115311 0.031862 -0.950026 +-0.119732 0.013277 -0.946169 +-0.116956 0.023495 -0.95581 +-0.121207 0.013384 -0.960631 +-0.115206 0.033599 -0.965693 +-0.122194 0.042528 -0.973164 +-0.109501 0.051305 -0.969549 +-0.106108 0.067989 -0.970754 +-0.117075 0.077062 -0.975093 +-0.10601 0.083071 -0.970754 +-0.108153 0.096173 -0.970272 +-0.105911 0.089891 -0.964247 +-0.112893 0.098274 -0.956293 +-0.107236 0.082972 -0.956534 +-0.112695 0.073005 -0.946893 +-0.118653 0.05789 -0.939902 +-0.124689 0.03842 -0.932912 +-0.054251 -0.024329 0.570632 +-0.111988 -0.029166 0.149335 +-0.102827 -0.050105 0.155093 +-0.116449 -0.007149 0.142486 +-0.109537 -0.085282 0.124806 +-0.107266 -0.068478 0.141081 +-0.091664 -0.065896 0.159362 +-0.101448 -0.006364 0.205763 +-0.100792 0.002775 0.176828 +-0.095502 0.023304 0.165658 +-0.106851 0.009749 0.155037 +-0.115526 0.014161 0.134647 +-0.109753 0.047412 0.082583 +-0.115809 0.052959 0.064582 +-0.107196 0.060068 0.03924 +-0.12426 0.057162 0.041541 +-0.135163 0.05902 0.014473 +-0.110525 0.02955 0.124647 +-0.097403 0.04055 0.131958 +-0.102391 0.040374 0.113425 +-0.093323 0.048946 0.102827 +-0.102411 0.045252 0.094047 +-0.094558 0.04884 0.075356 +0.092157 0.048065 -0.362567 +0.080423 0.052717 -0.331143 +0.090473 0.050157 -0.344858 +0.103947 0.049278 -0.349382 +0.115333 0.043908 -0.360673 +0.102967 0.047707 -0.360461 +0.154261 -0.028668 -0.468845 +0.162366 -0.034723 -0.485764 +0.158569 -0.022198 -0.486355 +0.169657 -0.042323 -0.506075 +0.174619 -0.052305 -0.527479 +0.173675 -0.036642 -0.528507 +0.176606 -0.031142 -0.553545 +0.169928 -0.022748 -0.529261 +0.161807 -0.010156 -0.52955 +0.162113 -0.015904 -0.507419 +0.149061 -0.034016 -0.453482 +0.043372 0.087032 0.607996 +0.050796 0.077545 0.612581 +0.057733 0.0781 0.627504 +0.05621 0.065484 0.618657 +0.060708 0.050856 0.625212 +-0.096239 0.027188 0.263362 +-0.090507 0.027446 0.23157 +-0.099124 0.006927 0.234635 +-0.079112 0.04604 0.233137 +-0.082624 0.036539 0.199058 +-0.073807 0.044645 0.17829 +-0.086095 0.035676 0.172444 +-0.091055 0.038716 0.152578 +-0.104198 -0.015731 0.243364 +-0.087677 0.048747 0.283971 +-0.076685 0.070177 0.293928 +-0.076986 0.059499 0.267173 +-0.066782 0.058222 0.233865 +-0.219107 0.005427 0.139559 +-0.227208 0.019759 0.120406 +-0.239844 0.01887 0.100013 +-0.238051 0.031753 0.104238 +-0.251669 0.043873 0.081306 +-0.241312 0.043693 0.110012 +-0.249499 0.05209 0.11651 +-0.266 0.059433 0.093409 +-0.26285 0.057929 0.122881 +-0.276557 0.058314 0.126641 +-0.262711 0.058578 0.145212 +-0.263273 0.054613 0.16916 +-0.062151 -0.104074 0.501967 +-0.09365 -0.103528 0.496735 +-0.120818 -0.100624 0.499053 +-0.140169 -0.09861 0.499327 +-0.121535 -0.102212 0.471815 +-0.120483 -0.100043 0.445758 +0.032502 0.105532 0.635204 +0.040234 0.101373 0.627981 +0.029099 0.103641 0.625923 +-0.159727 -0.11605 -0.943517 +-0.154575 -0.122851 -0.960149 +-0.145729 -0.122779 -0.948098 +-0.159244 -0.119119 -0.973887 +-0.14056 -0.118146 -0.97678 +-0.136031 -0.125444 -0.971237 +-0.124119 -0.121526 -0.966898 +-0.134307 -0.124716 -0.957739 +-0.13451 -0.12082 -0.938938 +-0.125296 -0.116442 -0.93725 +-0.132554 -0.117879 -0.901824 +-0.12834 -0.118523 -0.851712 +-0.137255 -0.119545 -0.85154 +-0.139205 -0.123264 -0.795694 +-0.147709 -0.116859 -0.829962 +-0.1577 -0.113684 -0.792993 +-0.156937 -0.108369 -0.848223 +-0.16437 -0.097422 -0.845784 +-0.162056 -0.10353 -0.890525 +-0.168228 -0.09362 -0.914112 +-0.165287 -0.106934 -0.925923 +-0.172012 -0.105366 -0.947133 +-0.177922 -0.090558 -0.947133 +-0.176295 -0.100282 -0.965211 +-0.17577 -0.091291 -0.975334 +-0.168679 -0.107233 -0.975334 +-0.159577 -0.109299 -0.978226 +0.19361 0.100996 -0.957016 +-0.346552 0.051592 -0.138698 +-0.05331 0.043614 -0.011406 +-0.043116 0.031079 -0.012221 +-0.044736 0.030689 -0.026708 +-0.036458 0.035374 -0.005666 +-0.034593 0.02256 -0.016369 +-0.026002 0.020196 -0.027749 +-0.032548 -0.000647 -0.037041 +-0.038473 0.015053 -0.034454 +-0.042106 0.031463 -0.054643 +-0.036354 0.025857 -0.083922 +-0.047462 0.04744 -0.085184 +-0.053157 0.058033 -0.124522 +-0.063386 0.064816 -0.085837 +-0.085277 0.075076 -0.086681 +-0.074292 0.068754 -0.050417 +-0.086456 0.067805 -0.017768 +-0.067251 0.059248 -0.01701 +-0.061836 0.050042 0.009179 +-0.072076 0.051032 0.033361 +-0.063817 0.04906 0.040277 +-0.070173 0.052519 0.067033 +-0.056287 0.052694 0.047223 +-0.043826 0.062622 0.049801 +-0.041395 0.049328 0.017863 +-0.026846 0.045526 -0.009819 +0.026111 -0.038364 -0.058814 +0.02288 -0.033403 -0.084919 +0.027925 -0.055292 -0.087379 +0.02033 -0.022247 -0.121654 +0.021046 -0.010807 -0.16115 +0.022959 -0.030884 -0.1634 +0.029038 -0.037487 -0.206713 +0.03026 -0.050994 -0.166212 +0.032675 -0.064246 -0.126926 +0.035877 -0.074808 -0.088427 +0.048094 -0.089411 -0.089855 +0.040517 -0.087569 -0.059881 +0.051296 -0.100298 -0.037443 +0.035125 -0.08673 -0.04151 +0.028149 -0.086287 -0.034555 +0.020779 -0.046423 -0.035166 +0.025827 -0.040847 -0.041556 +-5.2e-05 -0.062188 0.00548 +0.006463 -0.067002 -0.005033 +0.012015 -0.073985 -0.017958 +0.011292 -0.086761 -0.001085 +0.019728 -0.107513 0.001326 +-0.351987 0.047534 -0.115422 +-0.14288 0.119413 -0.96321 +-0.164423 0.119848 -0.961836 +-0.124308 0.111749 -0.956052 +-0.124703 0.115167 -0.964728 +-0.131241 0.115405 -0.971237 +-0.146413 0.109641 -0.974611 +-0.154631 0.117995 -0.971237 +-0.177461 0.114153 -0.9722 +-0.183398 0.103486 -0.976057 +-0.196445 0.10383 -0.971959 +-0.209582 0.088665 -0.970754 +-0.205996 0.096639 -0.964969 +-0.206554 0.088005 -0.954943 +-0.198745 0.100946 -0.957016 +-0.186213 0.111705 -0.961681 +-0.101613 0.044489 0.508518 +-0.091777 0.062148 0.497244 +-0.115934 0.045627 0.502282 +-0.065327 0.068468 0.494147 +-0.080758 0.081006 0.485764 +-0.072828 0.096906 0.474952 +-0.094039 0.092566 0.477121 +-0.105515 0.105185 0.467462 +-0.114767 0.087024 0.475675 +-0.128024 0.080515 0.470854 +-0.129064 0.06286 0.483867 +-0.141192 0.04135 0.481454 +-0.131264 0.042973 0.496583 +-0.086956 0.0366 0.517973 +-0.077142 0.031594 0.526258 +-0.062917 0.040509 0.517528 +-0.042954 0.041937 0.516578 +-0.051125 0.05472 0.504975 +-0.037893 0.067004 0.494885 +0.093484 -0.097607 0.529183 +0.073719 -0.095196 0.538101 +0.094082 -0.086854 0.544358 +0.063594 -0.088335 0.548857 +0.062627 -0.07834 0.556719 +0.043104 -0.080268 0.563468 +0.034656 -0.067018 0.583958 +-0.159365 0.009212 -0.972683 +-0.138816 0.025941 -0.973647 +-0.141228 -0.000439 -0.968586 +-0.138367 0.050982 -0.977021 +-0.124538 -0.006661 -0.956534 +-0.130607 -0.004067 -0.963041 +-0.129136 -0.034829 -0.963282 +-0.136905 -0.046366 -0.96979 +-0.13199 -0.067156 -0.975575 +-0.148774 -0.059115 -0.97437 +-0.161709 -0.072079 -0.977504 +-0.16425 -0.051076 -0.974611 +-0.175649 -0.042919 -0.972683 +-0.186474 -0.009328 -0.973165 +-0.175349 -0.002784 -0.973887 +-0.183278 0.02446 -0.975575 +-0.203258 0.038845 -0.975816 +-0.188311 0.049937 -0.977263 +-0.188748 0.069427 -0.977986 +-0.16395 0.05973 -0.977745 +-0.136642 0.068833 -0.977745 +0.020641 0.106222 0.68288 +0.010145 0.114881 0.68217 +-0.029 -0.004162 0.808506 +-0.018674 -0.009285 0.810771 +-0.027318 0.00999 0.810749 +-0.009358 -0.027252 0.807329 +-0.009189 -0.013674 0.811803 +0.000182 -0.009914 0.812958 +-0.008739 -0.00082 0.81441 +-0.026137 0.022133 0.811998 +-0.034725 0.029576 0.807259 +-0.036296 0.04573 0.803895 +-0.043479 0.035008 0.801776 +-0.050999 0.040011 0.794549 +-0.046173 0.020569 0.800655 +-0.049765 0.005415 0.797041 +-0.039102 0.000983 0.803895 +-0.030572 -0.017744 0.803895 +-0.033041 -0.031611 0.797041 +-0.021032 -0.035161 0.800678 +-0.010705 -0.050268 0.794618 +-0.010144 -0.039546 0.801847 +0.000295 -0.037069 0.804011 +-0.129301 0.086215 -0.97678 +-0.133015 -0.094692 0.454036 +-0.160025 -0.094814 0.509594 +-0.154706 -0.098454 0.492353 +-0.167264 -0.097792 0.475314 +-0.148605 -0.096707 0.473117 +-0.143549 -0.091627 0.451185 +-0.072193 0.039441 0.677409 +-0.069623 0.032875 0.660902 +-0.072019 0.033374 0.670516 +-0.073593 0.030982 0.680927 +-0.074948 0.036613 0.688621 +-0.071575 0.047963 0.690054 +-0.070167 0.058917 0.70771 +-0.068208 0.061066 0.690866 +-0.063995 0.074404 0.689656 +-0.065714 0.069965 0.676427 +-0.063559 0.081997 0.666975 +-0.065384 0.069769 0.659252 +-0.062969 0.074671 0.643834 +-0.066039 0.05651 0.649292 +-0.065441 0.044988 0.639524 +-0.067935 0.038771 0.651999 +-0.067598 0.025852 0.654161 +0.006898 0.113222 0.666528 +0.019509 0.109191 0.657762 +0.012014 0.113397 0.650796 +-0.346918 0.12142 -0.117927 +-0.354088 0.121599 -0.124121 +-0.344419 0.115564 -0.103046 +-0.34972 0.117279 -0.110488 +0.127701 -0.017526 -0.914112 +0.134572 -0.007901 -0.908327 +0.143269 -0.019543 -0.89748 +0.143442 -0.032239 -0.887839 +0.151011 -0.035966 -0.894347 +0.156411 -0.049333 -0.89507 +0.158344 -0.037714 -0.906158 +0.165572 -0.036508 -0.92544 +-0.02225 0.059766 0.501427 +-0.023436 0.07472 0.489154 +-0.011365 0.079551 0.484593 +-0.024381 0.086541 0.474952 +-0.015704 0.092085 0.464828 +-0.029443 0.098834 0.459284 +-0.029254 0.111213 0.441758 +-0.039324 0.10751 0.457116 +-0.050653 0.119321 0.4515 +-0.054991 0.107029 0.465552 +-0.0709 0.112091 0.464033 +0.020423 0.102461 0.608305 +0.035992 0.09389 0.606934 +0.025793 0.095655 0.598077 +0.051857 0.097225 0.633387 +0.04315 0.095805 0.617906 +-0.181442 -0.053912 -0.970754 +-0.185342 -0.0355 -0.968585 +-0.188407 -0.024348 -0.958462 +-0.192056 -0.015446 -0.969067 +-0.200678 0.00534 -0.970995 +-0.205676 0.015018 -0.963765 +-0.209533 0.02627 -0.97196 +-0.215892 0.046993 -0.970995 +-0.020731 -0.081957 -0.028567 +-0.015762 -0.05829 -0.029114 +-0.012329 -0.073985 -0.017958 +-0.020393 -0.046404 -0.035179 +-0.012687 -0.019453 -0.035064 +-0.011094 -0.036464 -0.029315 +-5.2e-05 -0.043146 -0.02248 +-0.006793 -0.067002 -0.005033 +-0.011606 -0.086761 -0.001326 +-0.01016 -0.099053 0.016992 +-0.019801 -0.107513 0.000844 +-0.03519 -0.122715 0.002285 +-0.031337 -0.113581 -0.011936 +-0.048416 -0.114586 -0.018931 +-0.031544 -0.100799 -0.024715 +-0.028404 -0.086268 -0.034568 +-0.035628 -0.086704 -0.041528 +-0.02362 -0.066445 -0.03714 +-0.025699 -0.040813 -0.041578 +-0.217927 0.056178 -0.96256 +-0.216728 0.068043 -0.96979 +-0.204529 0.079384 -0.975334 +-0.187076 0.088261 -0.978226 +-0.164926 0.099509 -0.977263 +-0.140782 0.098259 -0.976539 +-0.12071 0.098647 -0.974129 +-0.015554 0.023029 0.566772 +-0.019233 0.031936 0.534362 +-0.006303 0.039781 0.519301 +-0.017401 0.043994 0.515556 +-0.043958 0.004853 0.609394 +-0.051897 0.010736 0.631519 +-0.043078 0.012732 0.61846 +-0.045175 0.024826 0.613791 +-0.029703 0.021295 0.609428 +-0.015003 0.027943 0.601931 +-0.015397 0.021991 0.592477 +-0.005867 0.023916 0.567498 +-0.097654 0.15403 0.414501 +-0.106021 0.1509 0.422436 +-0.114099 0.14311 0.431965 +-0.105181 0.145039 0.431965 +-0.09795 0.134674 0.443848 +-0.094669 0.144955 0.431429 +-0.080782 0.140532 0.434579 +-0.089942 0.14945 0.421973 +-0.084901 0.150248 0.411978 +-0.068533 0.142851 0.413384 +-0.079138 0.149118 0.400054 +-0.068103 0.145349 0.392723 +-0.08706 0.150934 0.389497 +-0.094948 0.154109 0.402241 +-0.104885 0.155597 0.405945 +-0.123565 0.146136 0.401605 +-0.113336 0.154226 0.409681 +-0.122239 0.148005 0.414019 +-0.081095 0.052591 0.072127 +-0.082705 0.052338 0.053784 +-0.089286 0.058018 0.03567 +-0.098251 0.065585 0.010731 +0.28925 0.045602 -0.009567 +0.292529 0.034588 -0.011436 +0.284719 0.030641 0.007874 +0.298173 0.032736 -0.023181 +0.302759 0.030528 -0.010877 +0.312972 0.037178 -0.005232 +0.301493 0.022318 0.010571 +0.296961 0.01204 0.043347 +0.286506 0.011417 0.039962 +0.270972 0.000318 0.073557 +0.278498 0.015939 0.038776 +0.271804 0.025941 0.039055 +0.254717 0.020693 0.073311 +0.269021 0.037206 0.041245 +0.267648 0.047465 0.045042 +0.280242 0.053479 0.012775 +0.288229 0.065783 -0.000674 +0.287556 0.057176 -0.006856 +0.289871 0.057957 -0.019069 +0.295948 0.074193 -0.034474 +0.293578 0.055729 -0.035571 +0.299659 0.054995 -0.052561 +0.298705 0.042921 -0.044444 +0.310441 0.034345 -0.058644 +0.305132 0.033182 -0.038279 +0.235805 -0.050145 0.243698 +0.245376 -0.048187 0.236911 +0.246224 -0.046905 0.24834 +0.242491 -0.046658 0.225826 +0.255 -0.04119 0.231842 +0.266692 -0.032272 0.222715 +0.26212 -0.034758 0.239813 +0.263623 -0.029623 0.251835 +0.256126 -0.03921 0.250514 +0.246606 -0.046318 0.264945 +0.245716 -0.046049 0.285837 +0.236221 -0.052131 0.281348 +0.22488 -0.058924 0.302204 +0.225541 -0.054724 0.276795 +0.214081 -0.053728 0.27235 +0.225508 -0.052151 0.25575 +0.224536 -0.049797 0.239557 +0.214015 -0.045365 0.236305 +0.221176 -0.04779 0.228147 +0.216208 -0.041047 0.217972 +0.229561 -0.046609 0.221203 +0.240155 -0.043871 0.209954 +-0.059417 0.009418 0.787695 +-0.053243 -0.009739 0.790062 +-0.05706 -0.024749 0.778846 +-0.045051 -0.028037 0.790062 +-0.035286 -0.044335 0.787695 +-0.180683 -0.041219 -0.578831 +-0.175537 -0.031146 -0.553571 +-0.176602 -0.025983 -0.580031 +-0.172605 -0.036647 -0.528534 +-0.168859 -0.022753 -0.529288 +-0.161242 -0.015909 -0.507445 +-0.160737 -0.010161 -0.529577 +-0.14849 -0.00131 -0.529345 +-0.159038 -0.005362 -0.554497 +-0.154699 -0.003267 -0.581093 +-0.168224 -0.012792 -0.580668 +-0.175483 -0.022229 -0.612273 +-0.169777 -0.021693 -0.649493 +-0.17872 -0.03387 -0.646851 +-0.179299 -0.047129 -0.689553 +-0.183473 -0.048939 -0.644727 +-0.185221 -0.064888 -0.643884 +-0.184318 -0.052534 -0.60895 +-0.181709 -0.057628 -0.577707 +-0.179005 -0.07687 -0.577528 +-0.177507 -0.063217 -0.551419 +-0.170991 -0.068748 -0.52619 +-0.173551 -0.05231 -0.527506 +-0.168785 -0.042328 -0.506102 +0.092154 -0.05311 0.557856 +0.110348 -0.062122 0.551578 +0.118544 -0.049829 0.550614 +0.091429 -0.071075 0.555353 +0.112759 -0.07562 0.547722 +0.124087 -0.083092 0.539767 +0.123488 -0.090952 0.530619 +0.147208 -0.082727 0.532732 +0.163751 -0.086596 0.523111 +0.167215 -0.074061 0.532267 +0.181355 -0.068716 0.527983 +0.169725 -0.059203 0.536731 +0.16996 -0.044296 0.538715 +0.157488 -0.047446 0.542037 +0.139754 -0.03396 0.544878 +0.138367 -0.044594 0.546776 +0.117945 -0.037202 0.550143 +0.109874 -0.02361 0.549189 +0.093007 -0.030242 0.555939 +0.070108 -0.028687 0.562213 +0.069634 -0.03808 0.564859 +0.054196 -0.045559 0.577154 +0.068181 -0.04832 0.566305 +0.066004 -0.057483 0.565488 +0.064556 -0.067734 0.563709 +0.140035 -0.093514 -0.092145 +0.148605 -0.086131 -0.134493 +0.157508 -0.080765 -0.09277 +0.155489 -0.074654 -0.177276 +0.169056 -0.0578 -0.17712 +0.175859 -0.05251 -0.134745 +0.178984 -0.044802 -0.092636 +0.171196 -0.063998 -0.093178 +0.162918 -0.075809 -0.054259 +0.162813 -0.069618 -0.0175 +0.15164 -0.088465 -0.018245 +0.141603 -0.099259 0.009952 +0.13688 -0.101658 -0.018535 +0.1209 -0.109584 -0.023743 +0.130686 -0.101752 -0.053921 +0.117814 -0.101416 -0.091734 +0.091184 -0.103428 -0.091451 +0.019782 0.089341 0.584488 +-0.056232 0.093284 0.679412 +-0.072636 0.057242 0.724416 +-0.066049 0.072554 0.719436 +-0.059213 0.086421 0.724422 +-0.059279 0.085457 0.712483 +-0.051306 0.093238 0.699401 +-0.058462 0.085324 0.699207 +-0.047663 0.097322 0.687755 +0.005499 0.057805 -0.010951 +0.012745 0.065075 0.010335 +0.015692 0.053492 -0.012164 +0.007199 0.074492 0.042059 +0.026725 0.070387 0.044149 +0.042585 0.072901 0.084773 +0.044231 0.062609 0.049328 +0.056432 0.052674 0.046995 +0.041781 0.04931 0.017394 +0.036587 0.035342 -0.005884 +0.027221 0.045504 -0.010286 +0.018781 0.04154 -0.022529 +0.019554 0.029015 -0.029508 +0.012607 0.034629 -0.03064 +-6.1e-05 0.037879 -0.031299 +-6.3e-05 0.049097 -0.021972 +-0.005372 0.057808 -0.010711 +-0.015317 0.053505 -0.011692 +-0.01235 0.065084 0.010811 +-0.026311 0.0704 0.044621 +-0.007026 0.074499 0.042295 +-3e-05 0.078582 0.077556 +0.034475 0.02252 -0.016341 +0.027855 0.006828 -0.029477 +0.025882 0.020158 -0.027721 +0.070069 0.052493 0.067051 +0.063705 0.049027 0.040299 +0.1708 -0.048786 -0.016719 +0.178692 -0.035781 -0.052935 +0.15983 -0.064509 0.01593 +0.181799 -0.02331 -0.091181 +0.179477 7.1e-05 -0.089955 +0.172651 0.023084 -0.088665 +0.151966 -0.0452 0.070189 +0.15501 -0.056338 0.04692 +0.149717 -0.077178 0.043467 +-0.060997 0.014244 0.640455 +0.029437 0.020503 0.56901 +0.036427 0.012307 0.596246 +0.027474 0.026888 0.545481 +0.022206 0.024118 0.555512 +0.014975 0.022983 0.566758 +0.005541 0.023894 0.567491 +0.051751 0.010618 0.631502 +0.042587 0.012608 0.618431 +0.04362 0.004713 0.609833 +0.047187 -0.006745 0.609202 +0.044487 0.007977 0.591049 +0.04478 0.004342 0.572568 +0.039869 0.016341 0.56975 +0.035477 0.023909 0.55068 +0.231228 0.028966 0.248357 +0.232147 0.037525 0.231595 +0.219558 0.028188 0.243842 +0.247727 0.043746 0.215549 +0.231658 0.043805 0.209803 +0.231425 0.048107 0.183461 +0.217631 0.037675 0.203057 +0.207163 0.026016 0.196042 +0.206654 0.026409 0.219429 +0.200283 0.013862 0.233726 +0.208092 0.023203 0.238421 +0.207884 0.020504 0.256487 +0.197155 0.012116 0.273302 +0.206211 0.017357 0.277512 +0.203911 0.01663 0.302668 +0.21645 0.019107 0.281786 +0.22791 0.018111 0.286231 +0.229794 0.023032 0.265276 +0.244364 0.024711 0.252074 +0.25506 0.017995 0.254749 +0.260255 0.027033 0.238936 +0.274955 0.025766 0.220281 +0.262699 0.037508 0.219172 +0.263562 0.047989 0.194952 +-0.055273 -0.045936 -0.519443 +-0.057572 -0.052139 -0.497425 +-0.056619 -0.062531 -0.518394 +-0.061513 -0.042844 -0.478046 +-0.062629 -0.056837 -0.477045 +-0.067662 -0.05984 -0.460489 +-0.06743 -0.071226 -0.476496 +-0.075061 -0.082434 -0.476421 +-0.067654 -0.082092 -0.4964 +-0.071384 -0.093884 -0.51898 +-0.0619 -0.078097 -0.517919 +-0.057834 -0.072877 -0.544067 +-0.062274 -0.085678 -0.573733 +-0.056388 -0.067092 -0.573757 +-0.059578 -0.062913 -0.61318 +-0.054796 -0.050683 -0.574783 +-0.057039 -0.034769 -0.576001 +-0.054895 -0.039873 -0.546142 +-0.057761 -0.030459 -0.520797 +-0.064086 -0.016652 -0.522259 +-0.0623 -0.022607 -0.50001 +-0.068868 -0.018314 -0.480453 +-0.063753 -0.02879 -0.479277 +-0.065459 -0.034827 -0.462233 +-0.109923 0.097139 0.355728 +-0.090865 0.091656 0.35474 +-0.071542 0.095326 0.351671 +-0.056188 0.093387 0.339369 +-0.077145 0.084673 0.334731 +-0.079236 0.078124 0.313693 +-0.100936 -0.085951 -0.260342 +-0.120891 -0.085269 -0.26252 +-0.108267 -0.077994 -0.29988 +-0.131186 -0.08996 -0.220553 +-0.137402 -0.07889 -0.262806 +-0.152178 -0.065241 -0.261914 +-0.141676 -0.067763 -0.300371 +-0.143641 -0.056073 -0.33192 +-0.129503 -0.065865 -0.335172 +-0.121419 -0.06514 -0.367938 +-0.112603 -0.069717 -0.334966 +-0.095862 -0.069837 -0.333635 +-0.091719 -0.066681 -0.364809 +-0.079744 -0.06832 -0.330753 +-0.066743 -0.06431 -0.326443 +-0.072427 -0.072997 -0.293351 +-0.062856 -0.075002 -0.253308 +-0.081215 -0.082472 -0.256694 +-0.088531 -0.093675 -0.217257 +-0.074539 -0.096574 -0.174349 +-0.097136 -0.100976 -0.176111 +-0.106882 -0.103202 -0.134252 +-0.119835 -0.097944 -0.177229 +-0.139993 -0.088744 -0.177323 +0.018239 0.107256 0.671512 +-0.063667 -0.030882 -0.613891 +-0.063363 -0.02034 -0.57735 +-0.07406 -0.008564 -0.578235 +-0.067545 -0.011638 -0.548953 +-0.073747 -0.006272 -0.523751 +-0.08742 0.000865 -0.525118 +-0.080661 -0.003753 -0.502829 +-0.085059 -0.001355 -0.479605 +-0.077569 -0.009841 -0.481781 +-0.076309 -0.016679 -0.464386 +0.000655 0.093766 0.458566 +-0.002688 0.085095 0.474469 +0.01032 0.079544 0.484598 +0.022375 0.074701 0.489167 +0.023779 0.086522 0.474965 +0.029284 0.098801 0.459307 +0.016284 0.094475 0.458816 +0.003999 0.097856 0.43977 +0.008865 0.104204 0.421525 +-0.001514 0.098828 0.420243 +-0.002447 0.094977 0.395652 +-0.010454 0.103501 0.414762 +-0.019698 0.112569 0.404978 +-0.021112 0.113229 0.423026 +-0.034872 0.124633 0.422118 +-0.108977 -0.07772 -0.938697 +-0.109259 -0.095338 -0.938938 +-0.107241 -0.088549 -0.953641 +-0.113633 -0.098839 -0.923753 +-0.116069 -0.109289 -0.937974 +-0.115738 -0.112744 -0.967861 +-0.120446 -0.104066 -0.975093 +-0.111521 -0.09868 -0.966175 +-0.110599 -0.079589 -0.965452 +-0.118576 -0.073285 -0.973405 +-0.114012 -0.059077 -0.964005 +-0.119658 -0.041412 -0.959667 +-0.117299 -0.048889 -0.948579 +-0.122066 -0.040734 -0.934599 +-0.115623 -0.05857 -0.935082 +-0.113042 -0.071006 -0.923031 +-0.116876 -0.070528 -0.907364 +-0.114063 -0.084261 -0.914594 +-0.114361 -0.096332 -0.891874 +0.092611 -0.019001 0.551748 +0.121772 -0.017925 0.544638 +0.115293 -0.000121 0.538382 +0.097639 0.005006 0.540291 +0.071429 0.018691 0.538825 +0.0619 0.007477 0.546542 +0.052729 -0.000966 0.55956 +0.070088 -0.007715 0.552091 +0.070817 -0.020132 0.558359 +0.054188 -0.023873 0.571133 +-0.138607 0.02768 -0.35295 +-0.145511 0.020286 -0.352691 +-0.152296 0.016528 -0.338967 +-0.143987 0.017902 -0.368736 +-0.136642 0.01861 -0.394138 +-0.143563 0.00789 -0.390521 +-0.137355 0.00316 -0.416302 +-0.146884 -0.00545 -0.387504 +-0.144468 -0.015412 -0.390165 +-0.15644 -0.015421 -0.353648 +-0.161971 -0.027536 -0.325287 +-0.165474 -0.010869 -0.323954 +-0.17139 -0.004353 -0.293696 +-0.163127 0.005355 -0.322302 +-0.156493 0.021972 -0.321189 +-0.159237 0.031684 -0.291354 +-0.14683 0.03588 -0.320043 +-0.134195 0.048864 -0.318919 +-0.133129 0.041051 -0.336372 +-0.119072 0.0442 -0.348832 +-0.130775 0.036182 -0.351647 +-0.126896 0.038675 -0.36124 +-0.077792 -0.020144 -0.448556 +-0.069431 -0.035049 -0.448115 +-0.326873 0.070892 -0.095212 +-0.323667 0.076354 -0.088293 +-0.325844 0.086822 -0.08913 +-0.303517 0.08504 -0.050481 +-0.305062 0.071913 -0.060507 +-0.298297 0.05325 -0.051365 +-0.335226 0.071435 -0.105031 +-0.336521 0.091323 -0.101414 +0.095477 0.114936 0.354458 +0.074906 0.108128 0.353034 +0.083691 0.118559 0.354297 +0.055065 0.107161 0.357513 +0.036546 0.108676 0.369588 +0.052728 0.116812 0.361744 +0.062169 0.129533 0.366115 +0.070881 0.123564 0.35888 +0.088753 0.131816 0.363311 +0.090854 0.143024 0.374301 +0.100805 0.141939 0.376978 +0.109457 0.13928 0.37762 +0.118765 0.132885 0.374621 +0.109192 0.125534 0.364114 +0.109846 0.110711 0.356521 +0.12183 0.107697 0.360592 +0.109803 0.0971 0.355756 +0.113684 0.08144 0.361935 +0.090746 0.091617 0.354768 +0.071427 0.095289 0.351696 +0.139244 -0.091472 0.060615 +0.142666 -0.093081 0.038234 +0.130994 -0.108076 0.032687 +-0.01243 0.098552 0.594381 +-0.025239 0.095565 0.597988 +-0.075025 0.108167 0.353006 +-0.055184 0.107199 0.357485 +-0.052847 0.11685 0.361717 +-0.036657 0.108709 0.369566 +-0.025423 0.111393 0.38572 +-0.024164 0.101644 0.37025 +-0.014121 0.093803 0.35882 +-0.031916 0.096268 0.349002 +-0.04652 0.092123 0.322665 +-0.040688 0.086532 0.306737 +-0.061795 0.083175 0.305975 +-0.000933 0.105174 0.600462 +-0.035943 -0.066474 0.205451 +-0.0186 -0.064312 0.200394 +-0.08577 -0.092697 -0.476498 +-0.097479 -0.099416 -0.476688 +-0.090447 -0.106019 -0.497618 +-0.101793 -0.091054 -0.460571 +-0.110883 -0.10191 -0.477474 +-0.123514 -0.099711 -0.478696 +-0.118719 -0.11206 -0.49993 +-0.128373 -0.119869 -0.525079 +-0.112883 -0.122273 -0.52364 +-0.106856 -0.129589 -0.550984 +-0.098014 -0.118725 -0.522114 +-0.083467 -0.107549 -0.52012 +-0.077161 -0.107657 -0.546619 +-0.082954 -0.081004 -0.460122 +-0.081541 -0.070322 -0.446183 +-0.092255 -0.079879 -0.45023 +-0.104304 -0.079581 -0.445782 +-0.155674 -0.074622 -0.177299 +-0.16916 -0.057773 -0.177139 +-0.161643 -0.062189 -0.219637 +-0.16425 -0.050033 -0.260919 +0.127133 0.121437 0.370812 +0.139715 0.083369 0.375361 +0.134388 0.103125 0.368146 +-0.068844 0.012849 0.774859 +-0.063682 0.02886 0.78246 +-0.050887 0.056595 0.78944 +-0.058855 0.0443 0.785701 +-0.064074 0.048032 0.775737 +-0.060763 0.065345 0.769511 +-0.069069 0.051193 0.763654 +-0.071972 0.053638 0.751696 +-0.076084 0.035166 0.755922 +-0.080451 0.020839 0.742337 +-0.076253 0.015994 0.760403 +-0.07311 -0.002734 0.762646 +-0.075242 -0.016457 0.745698 +-0.067498 -0.021175 0.764266 +-0.059978 -0.038044 0.764266 +-0.009606 0.112575 0.67871 +-0.015597 0.118906 0.696268 +-0.00595 0.117643 0.685904 +0.226135 -0.009565 0.137964 +0.240308 -0.009492 0.116998 +0.238392 -0.021308 0.139073 +0.245799 0.004574 0.098227 +0.25536 -0.0058 0.098753 +0.268712 -0.011363 0.101984 +0.281644 -0.011411 0.106607 +0.268632 -0.021294 0.123851 +0.269432 -0.027605 0.148441 +0.253363 -0.027546 0.142696 +0.237528 -0.031788 0.163293 +0.237937 -0.038374 0.189058 +0.222965 -0.032136 0.185436 +0.211831 -0.032069 0.2031 +0.210709 -0.020394 0.184326 +0.203562 -0.00544 0.185949 +0.213017 -0.008303 0.161075 +0.218989 0.005389 0.139586 +0.217724 0.021935 0.143763 +0.227089 0.01972 0.120432 +0.237932 0.031715 0.104265 +0.239727 0.018832 0.100041 +0.130115 0.072367 -0.087024 +0.108208 0.07774 -0.08661 +0.095777 0.07914 -0.125732 +-0.345718 0.120033 -0.130569 +-0.3545 0.113702 -0.130478 +0.162066 0.04364 -0.087898 +0.14782 0.060596 -0.087193 +0.014973 -0.081226 0.124942 +0.1861 -0.077551 0.515917 +0.193564 -0.061189 0.519585 +0.202622 -0.050908 0.506478 +0.194808 -0.044467 0.52252 +0.189091 -0.027347 0.524711 +0.182835 -0.037088 0.532901 +0.171853 -0.028443 0.534873 +0.159701 -0.094919 0.51001 +0.175026 -0.088818 0.512794 +0.18688 -0.085619 0.498698 +0.112989 -0.099264 0.514738 +0.119816 -0.10079 0.499372 +0.12111 -0.102352 0.472425 +0.139744 -0.098748 0.499938 +0.154484 -0.098526 0.492574 +-0.057988 0.086278 0.737506 +-0.05054 0.098287 0.724297 +-0.040337 0.109009 0.725419 +-0.04241 0.106365 0.71133 +-0.034774 0.10754 0.698319 +-0.043208 0.101822 0.699208 +-0.038916 0.101242 0.690966 +-0.037643 -0.056629 0.774859 +-0.024399 -0.058749 0.782484 +-0.012425 -0.070703 0.772788 +-0.011827 -0.061132 0.78577 +0.000295 -0.058512 0.789555 +-0.060763 -0.048909 0.745698 +-0.050326 -0.053627 0.762646 +-0.039102 -0.066493 0.760403 +0.059026 0.08642 0.737646 +0.06025 0.086563 0.724562 +0.067086 0.072696 0.719576 +0.060317 0.085599 0.712624 +0.040503 0.101825 0.693342 +0.032757 0.103531 0.684267 +-0.0291 -0.018684 -0.042895 +-0.02906 -0.004299 -0.056925 +-0.026374 -0.038433 -0.059194 +-0.028475 -0.055575 -0.088465 +-0.02343 -0.033688 -0.086005 +-0.020775 -0.022888 -0.123089 +-0.024652 -0.013246 -0.084268 +-0.029232 0.005796 -0.083617 +-0.029056 0.016608 -0.121626 +-0.015647 0.006899 -0.037608 +-0.017185 -0.003649 -0.036607 +-0.008532 -0.002853 -0.039356 +-0.058424 0.097628 0.648 +-0.054499 0.096907 0.633642 +-0.059248 0.088844 0.638419 +-0.05795 0.078018 0.627512 +-0.007443 -0.071435 0.681152 +-0.116739 -0.136397 -0.580844 +-0.100306 -0.131872 -0.579544 +-0.097219 -0.130442 -0.616268 +-0.085158 -0.121117 -0.577172 +-0.072117 -0.104671 -0.575223 +-0.342027 0.053983 -0.116465 +-0.062924 0.055834 0.198102 +-0.06222 0.057004 0.16879 +-0.062482 0.064178 0.147523 +-0.056597 0.070898 0.121654 +-0.074757 0.060879 0.130228 +-0.084062 0.055142 0.11443 +-0.086826 0.049633 0.133312 +-0.050465 0.066241 0.237 +-0.036428 0.078589 0.278784 +0.130528 0.070492 -0.168451 +0.140569 0.067337 -0.127511 +0.107643 0.076923 -0.167086 +0.083619 0.075084 -0.165215 +0.073121 0.068867 -0.205393 +0.105061 0.144999 0.431993 +0.113979 0.143071 0.431993 +0.118559 0.131743 0.442671 +-0.010837 0.077735 0.109421 +0.01077 0.077721 0.10943 +0.022527 0.074634 0.135932 +0.03386 0.076146 0.114557 +0.056505 0.070872 0.121672 +-0.107441 -0.101702 0.109968 +-0.099315 -0.114582 0.096708 +-0.089676 -0.125385 0.083379 +-0.069075 -0.050206 -0.446984 +-0.073878 -0.060366 -0.446474 +-0.074237 -0.059456 -0.424272 +-0.03394 0.076172 0.114539 +-0.042417 0.072921 0.085 +-0.014258 -0.11014 0.033141 +-0.157132 -0.090422 0.430384 +-0.140529 -0.081659 0.429758 +-0.145454 0.048158 0.463138 +-0.017373 -0.070336 0.667195 +-0.127703 -0.087862 -0.461567 +-0.13624 -0.094361 -0.479914 +-0.146737 -0.087653 -0.48091 +-0.145564 -0.100101 -0.501844 +-0.155102 -0.101739 -0.525344 +-0.142492 -0.11326 -0.525774 +-0.138801 -0.12618 -0.553256 +-0.148871 -0.128621 -0.581038 +-0.133194 -0.135391 -0.581677 +-0.127663 -0.138176 -0.616978 +-0.150984 -0.068803 0.390182 +-0.078496 0.031985 0.709579 +-0.080563 0.025413 0.7244 +-0.077425 0.041899 0.723785 +-0.073202 0.055797 0.737495 +-0.06539 0.070966 0.745601 +-0.056146 0.084691 0.751708 +-0.018935 0.105176 0.619158 +-0.007126 0.109562 0.629151 +-0.051168 0.076338 0.775737 +-0.054422 0.081515 0.76366 +-0.046346 0.095969 0.755939 +-0.034454 0.104531 0.76042 +-0.037984 0.108279 0.742859 +-0.029419 0.116585 0.725169 +-0.047298 0.024671 -0.310259 +-0.037849 0.017816 -0.27972 +-0.040346 0.008987 -0.311486 +-0.038052 0.027864 -0.243939 +-0.030119 0.00961 -0.244461 +-0.02382 4.2e-05 -0.203348 +-0.027026 -0.008049 -0.245536 +-0.029351 -0.026807 -0.247007 +-0.031831 -0.016508 -0.282364 +-0.038859 -0.023333 -0.315602 +-0.037421 -0.00768 -0.313056 +-0.044946 0.004605 -0.332387 +-0.053967 0.004087 -0.358133 +-0.054656 0.017964 -0.342715 +-0.065584 0.033024 -0.344384 +-0.056152 0.029739 -0.330176 +-0.058364 0.04014 -0.310503 +-0.070722 0.05147 -0.311389 +-0.060798 0.050331 -0.280418 +-0.06604 0.059635 -0.245537 +-0.049933 0.044911 -0.244394 +-0.039882 0.038028 -0.203344 +-0.038989 -0.073927 0.742832 +-0.026679 -0.077612 0.752926 +-0.013508 -0.085225 0.741744 +-0.013247 -0.079702 0.757686 +0.000222 -0.077653 0.76359 +-0.123761 -0.022861 -0.937492 +-0.128272 -0.030546 -0.92279 +-0.132998 -0.017576 -0.914112 +-0.133664 -0.033246 -0.905194 +-0.132777 -0.045326 -0.894106 +-0.124311 -0.054024 -0.87316 +-0.124777 -0.059983 -0.896517 +-0.11425 -0.070799 -0.880902 +-0.105875 -0.070266 -0.84386 +-0.105368 -0.07996 -0.844737 +-0.09669 -0.083735 -0.796708 +-0.108292 -0.09176 -0.848477 +-0.114245 -0.103894 -0.85013 +-0.075698 -0.022685 -0.438931 +-0.066752 -0.020972 -0.416209 +-0.093239 0.007916 -0.466902 +-0.086006 0.003262 -0.460468 +-0.079904 0.004909 -0.444729 +-0.07522 -0.005856 -0.439366 +-0.066779 0.011585 -0.400071 +-0.061683 0.002995 -0.386192 +-0.058576 -0.009739 -0.386374 +-0.060099 -0.025929 -0.40223 +-0.214472 0.06587 -0.955087 +-0.211744 0.045888 -0.955087 +-0.205851 0.025451 -0.955328 +0.210488 0.024791 0.16943 +0.202298 0.011107 0.190126 +0.197022 -0.002238 0.208593 +0.195264 -0.013418 0.226204 +0.195517 0.000125 0.228806 +0.193247 0.002539 0.248068 +0.185983 -0.008188 0.266481 +0.190326 0.003492 0.269578 +0.184997 0.005073 0.293798 +0.06669 0.059844 -0.245514 +0.0619 0.051035 -0.280406 +0.050798 0.045614 -0.24438 +0.039176 0.029063 -0.243934 +-0.00915 0.088447 0.337107 +-0.005564 0.08412 0.313582 +-0.021113 0.08532 0.31136 +0.355453 0.089966 -0.127158 +0.350899 0.086456 -0.117554 +0.350983 0.097265 -0.137094 +0.072647 0.03954 0.677462 +0.072418 0.04809 0.690165 +0.075207 0.036698 0.688642 +0.073504 0.031041 0.680896 +-0.178961 -0.090729 -0.609801 +-0.172446 -0.097083 -0.578269 +-0.162075 -0.115543 -0.579879 +-0.164614 -0.10158 -0.551666 +-0.164655 -0.086899 -0.525697 +-0.163951 -0.073973 -0.503455 +-0.155571 -0.076365 -0.482167 +-0.160899 -0.062998 -0.48358 +-0.157142 -0.052747 -0.467235 +-0.163173 -0.047792 -0.48491 +-0.161891 -0.034728 -0.485791 +-0.06518 -0.059918 -0.358386 +-0.054486 -0.054171 -0.322075 +-0.045494 -0.039634 -0.317862 +-0.046159 -0.051222 -0.286305 +-0.037043 -0.045221 -0.248688 +-0.048844 -0.063367 -0.25063 +-0.051682 -0.075463 -0.211308 +-0.041886 -0.070545 -0.169579 +-0.056049 -0.085832 -0.171786 +-0.060066 -0.09468 -0.131873 +0.254005 -0.038433 0.194803 +0.269666 -0.031907 0.174783 +0.268033 -0.032302 0.201549 +0.278501 -0.020645 0.208564 +-0.073666 0.038526 -0.352224 +-0.076882 0.044216 -0.344481 +-0.090345 0.049949 -0.34488 +-0.079808 0.052262 -0.33116 +-0.086114 0.059059 -0.313538 +-0.101546 0.061002 -0.315492 +-0.094518 0.067326 -0.284767 +-0.106388 0.071042 -0.250493 +-0.084369 0.069042 -0.247735 +-0.073014 0.068797 -0.205774 +-0.065091 0.019151 0.646697 +-0.068711 0.018789 0.660834 +-0.022373 0.108586 0.644445 +-0.013195 0.113326 0.640083 +0.000977 0.116141 0.634141 +0.004099 0.080202 0.281578 +0.005222 0.084113 0.313587 +-1.8e-05 0.091348 0.36624 +0.014112 0.093784 0.358834 +0.009048 0.088435 0.337116 +0.020807 0.0853 0.31137 +0.040382 0.086506 0.306746 +0.036479 0.078562 0.278794 +0.051479 0.066209 0.237014 +0.036242 0.07127 0.236575 +0.035281 0.068899 0.193525 +0.019448 0.074097 0.237143 +0.05561 -0.052972 -0.32207 +0.06282 0.040489 0.517542 +0.051013 0.054687 0.504998 +0.042858 0.041918 0.516592 +0.065208 0.06843 0.494175 +0.037299 0.066972 0.494908 +0.02119 0.059746 0.501441 +0.02674 0.044809 0.514452 +0.038298 0.035426 0.525313 +0.071308 0.027007 0.533041 +0.077062 0.031587 0.526262 +0.086859 0.03658 0.517987 +0.11124 0.022703 0.520396 +0.101501 0.044457 0.508542 +0.115815 0.045589 0.502309 +0.091657 0.06211 0.497272 +0.080638 0.080966 0.485791 +0.07637 0.070151 0.293696 +0.087457 0.048722 0.283502 +0.077093 0.059473 0.2667 +0.099334 0.030242 0.29242 +0.092998 0.032041 0.26222 +0.090592 0.023644 0.230841 +0.082636 0.042793 0.245419 +0.067806 0.058189 0.233638 +0.064536 0.055795 0.198126 +0.04641 0.092096 0.322679 +0.061535 0.083149 0.305984 +0.079014 0.078099 0.313706 +0.07703 0.084647 0.334748 +0.091849 0.065857 0.328967 +0.108059 0.051446 0.346879 +0.101082 0.039429 0.317875 +0.110719 0.01355 0.310582 +0.047532 -0.049776 -0.286305 +0.046867 -0.038187 -0.317862 +0.040231 -0.021887 -0.315602 +-0.039663 0.064887 0.794549 +-0.04651 0.07132 0.785701 +-0.038541 0.084186 0.78246 +-0.032592 0.059025 0.801776 +-0.024175 0.069175 0.800655 +-0.007675 0.0712 0.803918 +-0.015757 0.080755 0.797041 +-0.00745 0.092215 0.790085 +-0.023277 0.089332 0.787695 +-0.029903 0.097925 0.774864 +-0.13845 3.5e-05 -0.507078 +-0.133365 0.003981 -0.52854 +-0.118126 0.005381 -0.527499 +-0.127763 0.005296 -0.553194 +-0.121695 0.003929 -0.580238 +-0.13855 0.0021 -0.580595 +-0.148539 -0.004026 -0.614203 +-0.141288 -0.008128 -0.65282 +-0.156359 -0.013014 -0.650997 +0.066578 -0.099193 -0.090488 +-0.020935 0.110536 0.762662 +-0.074793 -0.027179 0.725758 +-0.078767 -0.010184 0.725264 +-0.080834 0.008385 0.724145 +-0.078249 -0.000118 0.707505 +-0.076155 0.000473 0.689176 +-0.067835 -0.042762 0.725758 +-0.059417 -0.056343 0.725758 +-0.000277 0.032619 0.537537 +0.005267 0.039781 0.519301 +-0.051985 -0.063156 0.706182 +-0.048867 -0.068495 0.725135 +-0.037495 -0.077989 0.725432 +0.024103 0.101619 0.370269 +0.0319 0.096241 0.34902 +0.056128 0.09336 0.339387 +-0.109701 0.006933 -0.501361 +-0.102506 0.004261 -0.526311 +-0.095928 0.003411 -0.551193 +-0.088769 -0.000652 -0.579106 +-0.105482 0.003165 -0.579427 +-0.115801 0 -0.614731 +-0.111182 -0.006827 -0.655254 +-0.125404 -0.005933 -0.653705 +-0.179507 -0.072641 -0.973405 +-0.179736 -0.06291 -0.959185 +-0.177626 -0.071836 -0.943035 +-0.176403 -0.051299 -0.941831 +-0.170692 -0.036558 -0.92544 +-0.181173 -0.031149 -0.943277 +-0.188787 -0.011566 -0.948821 +-0.184485 0.00236 -0.937492 +-0.197436 0.007013 -0.952195 +0.22727 0.014598 0.31173 +0.238361 0.013681 0.289874 +0.247408 0.005987 0.292578 +0.250942 0.011887 0.271593 +0.260531 0.00639 0.26041 +0.061291 0.014473 0.640484 +0.064319 0.036648 0.631863 +0.064492 0.023752 0.636896 +0.065379 0.01969 0.646737 +0.067876 0.0264 0.654196 +0.046216 -0.02182 0.600769 +-0.048478 -0.033459 -0.350673 +-0.028058 -0.038793 -0.20707 +-0.022763 -0.03191 -0.164472 +-0.030063 -0.052021 -0.167285 +-0.03312 -0.064886 -0.128361 +-0.078161 0.023262 0.691248 +-0.080978 0.011701 0.692981 +-0.076344 0.010431 0.681702 +-0.064465 -0.038248 -0.423846 +0.025319 0.111368 0.385738 +0.303574 0.01443 0.063373 +0.310434 0.028986 0.052327 +0.307717 0.015772 0.090519 +0.314679 0.044944 0.019966 +0.310561 0.042088 0.055983 +0.303049 0.051605 0.07469 +0.306827 0.04219 0.097537 +0.299525 0.043386 0.128354 +0.305899 0.030051 0.126908 +0.304373 0.01851 0.147806 +0.306915 0.016735 0.123546 +0.302576 0.003439 0.11827 +0.295169 -0.009694 0.136612 +0.294152 -0.005943 0.112623 +0.294359 0.000894 0.081565 +0.323461 0.051886 -0.012258 +0.31762 0.048353 -0.001508 +0.314347 0.060279 0.004129 +0.049496 0.065008 0.164657 +0.031795 0.070223 0.160157 +0.017269 0.071324 0.154758 +-0.146941 -0.076726 -0.464747 +-0.158396 -0.12559 -0.614064 +0.015532 0.006863 -0.037583 +0.015801 0.017143 -0.036113 +0.006115 0.011659 -0.039677 +0.008433 -0.002873 -0.039343 +-4.7e-05 0.003617 -0.040655 +-0.006218 0.011679 -0.039691 +-0.012729 0.034655 -0.030658 +-0.12596 0.005521 -0.934599 +-0.122568 -0.004845 -0.942795 +-0.123125 -0.014672 -0.948338 +-0.123788 -0.025068 -0.956534 +-0.341135 0.114455 -0.093223 +0.283327 0.067105 0.019638 +0.293591 0.074868 0.000607 +0.303696 0.077585 0.000797 +0.301369 0.085086 -0.0127 +0.302858 0.089175 -0.033159 +0.304878 0.086785 -0.051677 +-0.141076 -0.005643 -0.486465 +-0.1314 0.000337 -0.482105 +-0.127069 -0.001274 -0.460449 +-0.123182 0.007158 -0.467277 +-0.119739 0.015043 -0.450152 +-0.114817 0.010324 -0.473312 +-0.10375 0.010983 -0.473422 +-0.012278 -0.088021 0.713298 +-0.013227 -0.084451 0.698266 +-0.026636 -0.078353 0.693522 +-0.016851 -0.07652 0.685949 +-0.006472 0.093335 0.584256 +-0.008884 0.08669 0.576962 +-0.018959 0.089208 0.584357 +-0.026719 0.077269 0.582716 +-0.035274 0.075271 0.589531 +-0.043146 0.064831 0.596409 +-0.043718 0.076945 0.600527 +-0.051105 0.077523 0.612557 +-0.046133 0.086744 0.60819 +-0.043245 0.096606 0.616926 +-0.037863 0.093977 0.607513 +0.075393 0.14152 0.374137 +0.086941 0.150895 0.389525 +0.104766 0.155557 0.405971 +0.055472 0.135163 0.378477 +0.099553 -0.102008 0.418153 +0.12026 -0.100115 0.445981 +0.124401 -0.087157 0.436601 +0.132895 -0.094732 0.454064 +0.14343 -0.091666 0.451211 +0.148486 -0.096747 0.473145 +0.167144 -0.097831 0.475341 +0.082814 -0.092688 0.359965 +-0.134686 -0.019262 -0.431128 +-0.130583 -0.011949 -0.44932 +-0.137 -0.01838 -0.453731 +-0.128557 -0.005102 -0.442871 +-0.151621 -0.012736 -0.486601 +-0.143161 -0.013686 -0.46922 +-0.145265 -0.023601 -0.458198 +-0.154182 -0.028671 -0.468872 +-0.14918 -0.034021 -0.45351 +-0.150857 -0.045031 -0.452753 +-0.147695 -0.037346 -0.42967 +-0.14553 -0.040588 -0.397805 +-0.143761 -0.031171 -0.410357 +-0.139706 -0.022557 -0.408004 +-0.133982 -0.011764 -0.420962 +-0.130189 -2.3e-05 -0.434238 +-0.130731 -0.059971 -0.402214 +-0.121147 -0.064038 -0.400681 +-0.125873 -0.068746 -0.427178 +-0.108189 -0.06546 -0.398883 +-0.095316 -0.066502 -0.397735 +-0.083395 -0.008128 -0.615211 +-0.083465 -0.018747 -0.656748 +-0.09549 -0.01114 -0.656054 +-0.10805 -0.017318 -0.701964 +-0.345052 0.050757 -0.075733 +-0.340852 0.040872 -0.077866 +-0.336268 0.045862 -0.051459 +-0.332708 0.03217 -0.080015 +-0.320226 0.028715 -0.057207 +-0.315513 0.029836 -0.034959 +-0.309902 0.030384 -0.019142 +-0.323412 0.038281 -0.031359 +-0.328846 0.05104 -0.026909 +-0.322347 0.05044 -0.011265 +-0.328773 0.070373 -0.02519 +-0.324824 0.085885 -0.025156 +-0.335409 0.090632 -0.048177 +-0.340413 0.105961 -0.07299 +-0.355039 0.079322 -0.09346 +-0.354729 0.070641 -0.094084 +-0.352009 0.069524 -0.083348 +-0.351945 0.060515 -0.095032 +-0.349183 0.058431 -0.083911 +-0.349528 0.052311 -0.095002 +-0.013355 0.039852 0.594693 +-0.024763 0.044374 0.593548 +-0.054537 0.031464 0.616147 +-0.041883 0.035996 0.605149 +-0.033948 0.049288 0.593243 +-0.0564 0.065484 0.618657 +-0.047072 0.09933 0.625654 +-0.038556 0.102139 0.627143 +0.038416 -0.043774 -0.248688 +-0.148015 -0.107669 -0.979672 +-0.133355 -0.105551 -0.979672 +-0.125761 -0.088709 -0.978708 +-0.158094 -0.022202 -0.486381 +-0.172153 -0.033047 -0.260095 +-0.164795 -0.038678 -0.296103 +-0.154686 -0.042198 -0.328648 +-0.146163 -0.046645 -0.362297 +-0.175247 -0.088746 0.512573 +-0.115347 0.043952 -0.360718 +-0.123524 0.03786 -0.375678 +-0.117734 0.034503 -0.397764 +-0.126675 0.028729 -0.39779 +-0.127998 0.019027 -0.419943 +-0.128992 0.067014 -0.016683 +-0.145541 0.056575 -0.015304 +-0.154535 0.052006 -0.049873 +-0.156895 0.041146 -0.013534 +0.030724 -0.025361 -0.247007 +0.361949 0.095031 -0.136579 +0.017074 -0.003681 -0.036584 +0.01259 -0.019473 -0.035051 +0.011123 -0.036474 -0.029308 +0.348279 0.123165 -0.119123 +0.351082 0.119023 -0.111683 +0.355221 0.109178 -0.109376 +0.355448 0.123344 -0.125317 +0.354377 0.127636 -0.135881 +-0.090973 0.143064 0.374274 +-0.075512 0.14156 0.374109 +-0.050079 0.136278 0.403542 +-0.051536 0.13747 0.389704 +-0.034916 0.125166 0.390231 +-0.055592 0.135202 0.378449 +-0.062288 0.129571 0.366088 +-0.071 0.123602 0.358853 +-0.083811 0.118598 0.35427 +-0.088873 0.131855 0.363284 +-0.100924 0.141979 0.37695 +-0.109577 0.13932 0.377592 +0.082637 0.018265 0.705134 +-0.039964 0.056346 0.594023 +-0.007824 0.077675 0.574045 +-0.052266 -0.100272 -0.03746 +-0.041446 -0.087651 -0.060252 +-0.061914 -0.113622 -0.024715 +-0.073924 -0.108272 -0.035007 +-0.097503 -0.11114 -0.028186 +-0.081836 -0.105332 -0.058315 +-0.092393 -0.103408 -0.091947 +-0.068174 -0.099288 -0.091332 +-0.049442 -0.089607 -0.090816 +-0.036659 -0.075099 -0.089509 +-0.065044 -0.123263 -0.010253 +-0.009836 0.115743 0.648737 +0.327237 0.060264 -0.098716 +0.317402 0.056792 -0.083903 +0.323016 0.066262 -0.090659 +0.325027 0.078099 -0.08949 +0.328238 0.072616 -0.096421 +0.336604 0.073106 -0.106272 +0.341772 0.073187 -0.117031 +0.345532 0.067716 -0.130501 +0.344925 0.062626 -0.120462 +0.341867 0.06099 -0.11061 +0.364758 0.09914 -0.149249 +-0.109966 0.110749 0.356492 +-0.109311 0.125573 0.364086 +-0.095597 0.114976 0.35443 +-0.118885 0.132924 0.374594 +-0.12195 0.107737 0.360565 +-0.134508 0.103165 0.368117 +-0.127253 0.121475 0.370784 +-0.125349 0.136668 0.384532 +-0.056701 0.132894 0.430754 +-0.05099 0.134502 0.41673 +0.039707 -0.057384 0.588539 +0.044777 -0.051595 0.583812 +0.046226 -0.037371 0.588 +0.045744 -0.025078 0.584625 +-0.353014 0.125885 -0.134685 +-0.171172 -0.082649 -0.978226 +-0.160534 -0.093928 -0.979672 +-0.174519 -0.08255 -0.925923 +-0.108082 -0.108063 -0.798143 +-0.11853 -0.1142 -0.831174 +-0.124871 -0.122406 -0.796678 +-0.149434 -0.05543 -0.451883 +0.104513 -0.119856 -0.006538 +0.0978 -0.111172 -0.028164 +0.073252 -0.108298 -0.034989 +0.080957 -0.10535 -0.058061 +0.131654 -0.0198 0.347079 +0.125752 0.000379 0.343833 +0.135014 0.009127 0.377787 +0.126958 0.034315 0.373538 +0.142655 0.022913 0.401574 +0.148499 0.040696 0.417274 +0.149856 0.024669 0.463786 +0.145439 0.003689 0.46021 +0.124828 -0.071875 0.412865 +0.122421 -0.065139 0.370851 +0.111641 -0.069488 0.340433 +0.117589 -0.047997 0.327629 +0.111689 -0.028426 0.285855 +0.120236 -0.024706 0.314792 +0.121132 -0.008466 0.319208 +-0.172876 -0.001797 -0.921102 +-0.166697 -0.021132 -0.914594 +-0.161038 -0.003711 -0.909773 +-0.140046 -0.007952 -0.908327 +-0.150225 -0.003921 -0.904471 +-0.149673 0.015229 -0.910738 +-0.136725 0.029957 -0.921102 +0.020953 0.113196 0.423049 +0.019571 0.112543 0.404996 +0.029089 0.111175 0.441785 +0.03473 0.124593 0.422144 +0.050871 0.134464 0.416757 +0.049959 0.136238 0.403569 +0.067984 0.14531 0.39275 +0.051416 0.137432 0.389731 +0.034804 0.125134 0.390255 +0.039183 0.107472 0.457142 +0.015921 -0.058296 -0.02911 +0.020451 -0.081964 -0.028563 +-0.338809 0.059569 -0.107587 +-0.343537 0.061005 -0.11919 +-0.340383 0.071566 -0.115761 +-0.130676 0.136435 0.392085 +-0.139835 0.118599 0.388783 +-0.135497 0.131855 0.40028 +-0.140317 0.11908 0.41144 +-0.131158 0.137399 0.411849 +-0.127783 0.139086 0.421009 +0.075004 -0.077493 0.30713 +0.093165 -0.079862 0.333638 +0.102578 -0.060463 0.304957 +0.078029 0.023319 0.691211 +-0.057586 -0.034515 -0.389729 +-0.132363 0.124624 0.43241 +-0.123592 0.137482 0.430947 +-0.118679 0.131781 0.442643 +0.002832 0.090679 0.579852 +-0.141763 0.100521 0.378563 +-0.355368 0.062627 -0.114814 +-0.349396 0.078605 -0.105548 +-0.288757 0.056509 -0.018076 +-0.286935 0.056323 -0.006271 +-0.288629 0.044749 -0.008982 +-0.287855 0.065227 -0.000294 +-0.280113 0.053221 0.012952 +-0.267767 0.047503 0.045014 +-0.269139 0.037245 0.041218 +-0.254836 0.020732 0.073284 +-0.271923 0.02598 0.039028 +-0.284591 0.030382 0.008051 +-0.295296 0.031881 -0.01158 +-0.30377 0.031437 -0.037083 +-0.297344 0.041176 -0.043246 +-0.292216 0.053984 -0.034375 +-0.294587 0.072448 -0.033277 +-0.301496 0.08743 -0.031962 +-0.300253 0.083639 -0.011707 +-0.302827 0.076435 0.001586 +-0.292969 0.074014 0.001193 +-0.281513 0.06279 0.019897 +0.086165 0.000232 0.696923 +0.008936 0.071248 0.803965 +0.000872 0.107344 0.613001 +-0.351867 0.045028 -0.160324 +-0.184334 -0.090987 0.478655 +-0.180866 -0.093797 0.455579 +-0.192861 -0.084703 0.435922 +-0.175791 -0.091508 0.432581 +-0.17016 -0.086396 0.41059 +-0.101383 -0.071651 -0.425071 +-0.117583 -0.078589 -0.445556 +-0.129201 -0.076726 -0.446516 +-0.062555 -0.045563 -0.390664 +-0.070265 -0.057484 -0.394165 +-0.081778 -0.063749 -0.395622 +-0.018647 0.109092 0.657665 +-0.01261 0.109655 0.669207 +0.0612 -0.113649 -0.024697 +0.064575 -0.12329 -0.010235 +0.047695 -0.114606 -0.018917 +0.030815 -0.113587 -0.011691 +0.030815 -0.100812 -0.024706 +0.045019 -0.011214 0.573057 +-0.137942 -0.072298 -0.448699 +-0.144563 -0.066044 -0.450807 +-0.025506 0.112792 0.696714 +-0.017276 0.122161 0.725792 +-0.021595 0.119803 0.709461 +-0.142078 -0.058424 -0.430818 +-0.056606 -0.128518 0.003003 +-0.08336 -0.12659 0.001076 +-0.097366 -0.127499 0.015291 +-0.103809 -0.119817 -0.006566 +0.039376 0.10617 0.663733 +0.028632 0.106834 0.66425 +0.033957 0.107383 0.64946 +0.016359 0.043991 0.515559 +0.044878 0.100847 0.681489 +0.045223 0.103968 0.670256 +0.052914 0.104231 0.65826 +0.044621 0.10681 0.652899 +0.076213 0.010488 0.681667 +0.071646 0.023293 0.67267 +0.362776 0.097661 -0.123083 +0.359574 0.088524 -0.112757 +0.312943 0.044951 -0.07454 +0.308352 0.056671 -0.069257 +0.306424 0.073658 -0.061704 +0.314519 0.093868 -0.068159 +0.324215 0.107993 -0.078378 +0.321862 0.097355 -0.08002 +0.329791 0.096935 -0.09254 +0.327202 0.088566 -0.090317 +0.321923 0.034752 -0.080185 +0.024801 0.001349 -0.202991 +0.031492 0.011056 -0.244461 +0.028399 -0.006603 -0.245536 +0.033205 -0.015061 -0.282364 +-0.028547 0.103573 0.625856 +-0.032026 0.105467 0.63514 +-0.081198 0.054688 0.090468 +0.058176 0.097731 0.647868 +0.060032 0.093784 0.669735 +0.063961 0.082176 0.666919 +0.06654 0.070166 0.676537 +0.065813 0.070004 0.659307 +0.066309 0.056881 0.649331 +0.062987 0.07486 0.643841 +0.059015 0.089006 0.6384 +-0.03838 0.035432 0.525308 +-0.071382 0.027007 0.533041 +-0.009674 0.106762 0.611441 +-0.019029 0.103085 0.608624 +0.039222 0.019263 -0.27972 +0.362573 0.106105 -0.142558 +0.357631 0.10375 -0.131248 +0.353224 0.101317 -0.116185 +0.296483 0.061471 0.059544 +0.291315 0.057051 0.098559 +0.276438 0.058275 0.126668 +0.289726 0.052774 0.12868 +0.290853 0.046799 0.150876 +0.310791 0.072738 0.004206 +0.303663 0.06729 0.025 +0.284757 0.064199 0.058181 +-0.139835 0.103654 0.435928 +-0.132604 0.110644 0.44557 +-0.130916 0.094977 0.458971 +-0.124074 0.116031 0.45141 +-0.111929 0.11973 0.45508 +-0.100119 0.120694 0.456286 +-0.086715 0.123262 0.455026 +-0.360632 0.093308 -0.135502 +-0.349583 0.084734 -0.116476 +-0.358255 0.086801 -0.111672 +-0.131878 0.000476 -0.92062 +-0.12911 0.019234 -0.927128 +0.198889 -0.026778 0.227738 +0.192185 -0.023207 0.243713 +0.188352 -0.031844 0.264659 +0.185173 -0.020391 0.264729 +0.176599 -0.017861 0.287774 +-0.013024 -0.088241 0.72662 +-0.0247 -0.08469 0.720404 +-0.033508 -0.078855 0.707401 +-0.0416 -0.069884 0.69463 +-0.027312 0.044822 0.514443 +0.018951 0.105409 0.618947 +0.057516 0.093339 0.67944 +0.278125 0.048336 0.17281 +0.263154 0.054575 0.169187 +0.262592 0.058539 0.14524 +0.247085 0.054634 0.163441 +0.233057 0.048502 0.156696 +0.282102 0.010812 0.218659 +0.288074 0.024504 0.19717 +0.297528 0.02164 0.172297 +0.290382 0.036594 0.173918 +0.065553 0.045494 0.639548 +0.068224 0.03931 0.65204 +0.069891 0.033191 0.660931 +-0.083804 0.074907 -0.165948 +-0.062697 0.064238 -0.16485 +-0.045673 0.048194 -0.163564 +-0.03268 0.028352 -0.161928 +-0.127843 0.065599 -0.253296 +-0.118913 0.072745 -0.210048 +-0.130514 0.070531 -0.168479 +-0.107684 0.076855 -0.167466 +-0.096156 0.078963 -0.126465 +-0.083653 -0.133012 0.031918 +-0.10907 -0.123137 0.031923 +-0.118079 -0.11643 0.049941 +-0.159349 0.041309 -0.255529 +-0.155791 0.0504 -0.21274 +-0.146525 0.05484 -0.254917 +-0.162423 0.043235 -0.170205 +-0.148711 0.058947 -0.169549 +-0.1406 0.067376 -0.127538 +-0.133779 0.058198 -0.289618 +-0.11935 0.057318 -0.31754 +-0.168086 0.02405 -0.256279 +-0.173604 0.00463 -0.257509 +-0.17464 0.014528 -0.214836 +-0.179462 0.002306 -0.17347 +-0.172535 0.024622 -0.171551 +-0.168602 0.03471 -0.128899 +-0.06391 0.131373 0.441954 +0.095027 -0.048825 0.262066 +0.103824 -0.036545 0.269255 +0.104098 -0.015764 0.243629 +-0.0991 0.017944 -0.450663 +0.262731 0.05789 0.122908 +0.265882 0.059393 0.093437 +0.249381 0.05205 0.116538 +0.241193 0.043654 0.110039 +0.235215 0.046569 0.132123 +0.222589 0.036845 0.14968 +0.146018 0.020217 -0.352553 +0.143756 0.007836 -0.390439 +0.14439 0.017838 -0.368618 +0.139017 0.027615 -0.35283 +0.12709 0.038621 -0.361158 +0.131022 0.036125 -0.351555 +0.355028 0.04387 -0.128252 +0.353348 0.049278 -0.116616 +0.349146 0.056385 -0.106308 +0.35089 0.054056 -0.096199 +0.350544 0.060176 -0.085108 +0.346414 0.052502 -0.07693 +0.337629 0.047607 -0.052655 +0.342214 0.042617 -0.079063 +0.33407 0.033915 -0.081211 +0.341084 0.037101 -0.099069 +0.344392 0.039642 -0.121249 +-0.141336 -0.051267 -0.399824 +0.298792 0.005093 0.16812 +0.290602 -0.00859 0.188816 +0.293927 -0.009816 0.162202 +0.283459 -0.021475 0.155186 +0.27585 -0.003524 0.237141 +0.283366 -0.005735 0.214482 +-0.351907 0.099595 -0.115108 +-0.34249 0.092899 -0.112093 +-0.345768 0.096557 -0.122526 +-0.349666 0.095542 -0.136017 +-0.356314 0.102028 -0.130171 +-0.312351 0.036325 -0.004647 +-0.305342 0.029027 -0.008073 +0.191614 0.011738 0.323365 +0.203097 0.014525 0.328262 +0.202498 0.011696 0.352268 +0.214997 0.013491 0.332878 +0.226236 0.008727 0.336797 +0.351124 0.080663 -0.106541 +0.356378 0.081055 -0.094596 +0.353367 0.071267 -0.084535 +0.356095 0.072365 -0.095294 +0.35739 0.073907 -0.111625 +0.337536 0.088233 -0.102831 +0.343807 0.094623 -0.113171 +-0.02455 0.00862 -0.161409 +-0.02085 -0.011834 -0.162222 +-0.012323 0.119858 0.745726 +0.154903 -0.108342 -0.84821 +0.157955 -0.103488 -0.89052 +0.162337 -0.097395 -0.84577 +0.160152 -0.106884 -0.925923 +0.163093 -0.093571 -0.914112 +0.169384 -0.0825 -0.925923 +0.163187 -0.078345 -0.907845 +0.158667 -0.064508 -0.89748 +0.162831 -0.075833 -0.88042 +0.167599 -0.073578 -0.841037 +0.167119 -0.084966 -0.841937 +0.172359 -0.091098 -0.788958 +0.179314 -0.084468 -0.735822 +0.174054 -0.098491 -0.737941 +0.172653 -0.108092 -0.692848 +0.166863 -0.110119 -0.739513 +0.156392 -0.119247 -0.743145 +0.157734 -0.113673 -0.792971 +0.146709 -0.116839 -0.829945 +0.139238 -0.123252 -0.795671 +0.135221 -0.119519 -0.851526 +0.126098 -0.118567 -0.85216 +0.128453 -0.117836 -0.90182 +0.120161 -0.116392 -0.93725 +0.129375 -0.12077 -0.938938 +0.129172 -0.124665 -0.957739 +0.140594 -0.122729 -0.948098 +0.14944 -0.122801 -0.960149 +0.154592 -0.115999 -0.943517 +0.166877 -0.105317 -0.947133 +0.065331 0.074522 0.689774 +0.069246 0.061208 0.691007 +0.071205 0.059059 0.707851 +0.35675 0.064282 -0.116065 +0.353311 0.062239 -0.096241 +0.355051 0.062522 -0.127898 +0.361289 0.071473 -0.127738 +0.35018 0.077184 -0.123737 +0.20152 -0.056893 0.293143 +0.203629 -0.049299 0.268706 +0.194583 -0.041604 0.266002 +0.204535 -0.04329 0.248854 +0.205009 -0.037103 0.231537 +0.140183 -0.036419 -0.8667 +0.134024 -0.037348 -0.836944 +0.143458 -0.038358 -0.836309 +0.122109 -0.041502 -0.8244 +0.125832 -0.031999 -0.795256 +0.117287 -0.025912 -0.751044 +0.128883 -0.023115 -0.748801 +0.134097 -0.01391 -0.699996 +0.139656 -0.024143 -0.746943 +0.152452 -0.029692 -0.74438 +0.146039 -0.034038 -0.792764 +0.153367 -0.04399 -0.821778 +0.163786 -0.047167 -0.789462 +0.160572 -0.053136 -0.836354 +0.165379 -0.062623 -0.837463 +0.159322 -0.056693 -0.872748 +0.135524 -0.036067 -0.889768 +0.128481 -0.032994 -0.905194 +0.127864 -0.044391 -0.893624 +0.12024 -0.059215 -0.895553 +0.120564 -0.052687 -0.872655 +0.109133 -0.059543 -0.840626 +0.116183 -0.050045 -0.839212 +0.109217 -0.043154 -0.796386 +-0.019678 0.02905 -0.029532 +-0.015918 0.01718 -0.036137 +-0.027782 0.104204 0.685235 +-0.018875 0.106096 0.678748 +-0.044037 0.103878 0.670194 +-0.038982 0.102331 0.681333 +-0.128709 0.010087 -0.432263 +-0.12282 0.016837 -0.434716 +-0.115201 0.022097 -0.434582 +-0.111437 0.030062 -0.419783 +-0.105629 0.024339 -0.434984 +-0.093734 0.023146 -0.434976 +-0.181137 -0.019254 -0.175074 +-0.162184 0.043679 -0.087926 +-0.175245 -0.015389 -0.25902 +-0.177955 -0.02705 -0.217951 +-0.177569 -0.03962 -0.176278 +0.035154 -0.122722 0.002771 +-0.052378 0.104259 0.65844 +-0.043756 0.106801 0.652945 +-0.03289 0.107281 0.649359 +0.093246 0.049027 0.102835 +0.102315 0.040455 0.113433 +0.081108 0.054715 0.090481 +0.083986 0.055223 0.114438 +0.074667 0.060907 0.130241 +0.086741 0.049708 0.133324 +0.090962 0.038731 0.152601 +0.097319 0.040624 0.13197 +0.110442 0.029584 0.124656 +0.11543 0.014141 0.134661 +-0.146583 0.097629 0.39423 +-0.147066 0.097629 0.408934 +-0.144415 0.099798 0.424119 +-0.075479 0.127276 0.449668 +-0.137907 0.071356 0.459767 +-0.145378 0.063402 0.446028 +0.002666 0.099924 0.594166 +-0.208136 -0.06588 0.463823 +-0.213936 -0.055034 0.444634 +-0.206495 -0.070744 0.440044 +-0.214464 -0.036885 0.448758 +-0.221066 -0.043897 0.422246 +-0.2282 -0.032688 0.395547 +-0.227826 -0.044995 0.392802 +-0.236249 -0.044725 0.363377 +-0.221761 -0.057822 0.388709 +-0.212382 -0.068579 0.384699 +-0.20756 -0.072435 0.413562 +-0.187 -0.085581 0.498671 +-0.197969 -0.077029 0.482777 +-0.206481 -0.058685 0.487615 +-0.203208 -0.050881 0.507905 +-0.20808 -0.037902 0.491988 +-0.201669 -0.018703 0.494444 +-0.210263 -0.026948 0.47232 +-0.208053 -0.017687 0.451214 +0.16996 -0.027323 0.310191 +0.170357 -0.015106 0.311774 +0.166048 -0.012376 0.331959 +0.17441 -0.003851 0.314699 +0.181753 0.005421 0.318704 +-0.1098 0.054429 -0.334104 +-0.14562 0.080033 0.435592 +0.060488 -0.068938 0.271743 +0.078781 -0.059829 0.264962 +0.092697 -0.040887 0.223772 +0.093348 -0.035422 0.193415 +0.098751 -0.019246 0.193125 +0.100697 0.002736 0.176856 +0.101353 -0.006403 0.205791 +0.095902 0.006341 0.221401 +0.038794 -0.006233 -0.313056 +0.041719 0.010433 -0.311486 +0.066881 -0.020749 -0.416186 +0.029509 -0.087462 0.126235 +0.022217 -0.076703 0.144776 +0.032796 -0.071917 0.162044 +0.011425 -0.071799 0.162332 +0.002712 -0.068146 0.176001 +0.070781 0.112051 0.464061 +0.054872 0.10699 0.465579 +0.072708 0.096866 0.474979 +0.050534 0.119282 0.451526 +0.093919 0.092528 0.477149 +0.114648 0.086984 0.475703 +0.105395 0.105146 0.46749 +0.111811 0.119691 0.455108 +0.1 0.120655 0.456313 +0.097831 0.134636 0.443876 +0.086595 0.123223 0.455053 +0.07536 0.127236 0.449695 +0.080663 0.140493 0.434606 +0.06379 0.131334 0.441982 +0.056581 0.132855 0.430781 +0.179852 0.005927 0.340541 +0.048671 0.026118 -0.310259 +0.059727 0.041338 -0.310499 +0.027022 0.037246 0.523155 +-0.080366 0.042976 -0.359844 +-0.072915 0.03363 -0.369455 +-0.070227 0.013891 -0.415307 +-0.069241 0.022839 -0.390911 +-0.076875 0.030483 -0.393719 +-0.085119 0.027745 -0.418972 +-0.086507 0.036825 -0.396545 +-0.096641 0.042094 -0.387468 +-0.087837 0.044531 -0.374711 +-0.092277 0.048103 -0.362594 +-0.103086 0.047746 -0.360488 +-0.104066 0.049317 -0.349409 +0.275432 0.062709 0.0551 +0.269288 0.055547 0.049494 +0.25155 0.043835 0.081332 +0.312765 0.085286 -0.010822 +-0.006119 0.113561 0.764306 +0.000738 0.103834 0.778899 +0.056372 -0.128537 0.003258 +0.044313 -0.130652 0.018679 +0.05757 -0.134243 0.033141 +0.083339 -0.126623 0.001097 +-0.155492 -0.028329 0.356208 +-0.160749 -0.032802 0.345004 +-0.161044 -0.021111 0.346178 +-0.166167 -0.012336 0.331933 +-0.164113 -0.010625 0.349268 +-0.170574 -0.001596 0.353845 +-0.16219 -0.006465 0.362443 +-0.164373 -0.002133 0.375173 +-0.154414 -0.01074 0.370753 +-0.163482 -0.053438 0.350923 +-0.160466 -0.043558 0.34741 +-0.165475 -0.036245 0.329176 +-0.18622 -0.077512 0.51589 +-0.193917 -0.061157 0.520285 +-0.195394 -0.04444 0.523948 +-0.176073 0.003874 0.375161 +-0.167484 0.004652 0.390015 +-0.172233 0.009446 0.407131 +0.075831 -0.027037 0.725898 +0.068873 -0.04262 0.725898 +0.061801 -0.048767 0.745837 +-0.27555 0.062748 0.055073 +-0.269406 0.055586 0.049467 +0.103573 0.004244 -0.526283 +0.096997 0.003408 -0.551167 +0.088488 0.000855 -0.525091 +0.106551 0.00317 -0.5794 +0.089838 -0.000647 -0.579079 +0.084256 -0.008196 -0.615646 +0.075129 -0.008559 -0.578207 +0.064432 -0.020335 -0.577323 +0.068614 -0.011633 -0.548927 +0.065155 -0.016647 -0.522231 +0.074816 -0.006275 -0.523723 +0.081531 -0.003771 -0.502802 +0.078044 -0.009858 -0.481754 +0.119195 0.005364 -0.527473 +0.134434 0.003972 -0.528513 +0.128831 0.005293 -0.553166 +0.139619 0.002105 -0.580567 +0.122764 0.003934 -0.580212 +0.116662 -6.7e-05 -0.615168 +0.349613 0.067725 -0.140039 +0.356089 0.091025 -0.092513 +0.353287 0.086051 -0.082628 +0.068413 0.142812 0.41341 +0.206081 -0.033746 0.219483 +0.078094 -0.064043 0.179812 +0.082596 -0.049383 0.190776 +0.067026 -0.061547 0.206463 +0.097546 -0.038496 0.172632 +0.043016 -0.069454 0.25707 +0.056181 -0.066368 0.240689 +0.051476 -0.065838 0.21055 +0.035925 -0.066514 0.205479 +0.04313 -0.069236 0.184395 +0.050298 -0.074615 0.161752 +0.055473 -0.086776 0.145536 +0.066807 -0.080225 0.156563 +0.080701 -0.090169 0.145461 +0.078786 -0.075462 0.161242 +0.090862 -0.065936 0.159389 +0.106573 -0.068518 0.141109 +0.102218 -0.050143 0.15512 +0.111741 -0.029199 0.149358 +0.112008 -0.070891 -0.906881 +0.11057 -0.07076 -0.880396 +0.102978 -0.080562 -0.84563 +0.103886 -0.06994 -0.844271 +0.096618 -0.061591 -0.797183 +0.0882 -0.060651 -0.752721 +0.091109 -0.04912 -0.75201 +0.08615 -0.033149 -0.705572 +0.09666 -0.040182 -0.752628 +0.106557 -0.032314 -0.752003 +0.14956 -0.001313 -0.529318 +0.144855 -0.124841 -0.745393 +0.149778 -0.127288 -0.700318 +0.177309 -0.101695 -0.647878 +0.167115 -0.118095 -0.650531 +0.159465 -0.125585 -0.614036 +0.154711 -0.128161 -0.654369 +0.139654 -0.133976 -0.656646 +0.128731 -0.138171 -0.616951 +0.126036 -0.135417 -0.659802 +0.111818 -0.132281 -0.660855 +0.124126 -0.130883 -0.706812 +0.122834 -0.126243 -0.750746 +0.132821 -0.126997 -0.748729 +0.058829 -0.030454 -0.520769 +0.06317 -0.022602 -0.499983 +0.064227 -0.028785 -0.47925 +0.069343 -0.018317 -0.480426 +0.076388 -0.016697 -0.464359 +0.077672 -0.020169 -0.448528 +0.353537 0.0987 -0.091601 +0.346735 0.096378 -0.073406 +0.341774 0.107706 -0.074186 +0.336771 0.092377 -0.049375 +0.326186 0.08763 -0.026353 +0.330134 0.072118 -0.026387 +0.330207 0.052784 -0.028106 +0.324773 0.040026 -0.032555 +0.124965 -0.02669 0.131936 +0.116345 -0.007176 0.142504 +0.106752 0.009716 0.15506 +0.095406 0.023264 0.165686 +0.09808 -0.130509 -0.616704 +0.099527 -0.124822 -0.662285 +0.087129 -0.110283 -0.661788 +0.102193 -0.117634 -0.710458 +0.104738 -0.111843 -0.754197 +0.113192 -0.120451 -0.752962 +0.120129 -0.121547 -0.798324 +0.116907 -0.114396 -0.832544 +0.108758 -0.085321 0.124834 +0.121176 -0.06699 0.122271 +0.13404 -0.064346 0.105192 +0.130046 -0.046107 0.118861 +0.339622 0.118009 -0.112037 +0.334976 0.116204 -0.088929 +0.342497 0.1162 -0.094419 +0.073351 0.008257 0.671288 +0.10554 -0.092843 -0.849852 +0.095638 -0.08453 -0.799001 +0.090968 -0.087454 -0.754633 +0.088128 -0.07297 -0.753181 +0.078976 -0.058512 -0.707107 +-0.075424 0.009123 -0.435344 +0.316876 0.03158 -0.036155 +0.086249 0.027853 0.199251 +0.08641 0.035636 0.172472 +0.075024 0.044607 0.178318 +0.063516 0.056965 0.168818 +-0.106758 -0.031887 -0.749253 +-0.096861 -0.039755 -0.749878 +-0.110329 -0.043009 -0.794095 +-0.118654 -0.050742 -0.838319 +-0.111159 -0.06052 -0.840215 +0.162173 -0.024273 -0.695147 +0.163692 -0.037602 -0.74226 +0.172937 -0.048276 -0.73928 +0.180361 -0.047124 -0.689526 +0.178379 -0.05903 -0.736715 +0.180512 -0.072165 -0.735769 +0.174036 -0.067408 -0.788005 +-0.01866 0.041565 -0.022306 +0.160106 -0.005357 -0.554471 +-0.33064 0.11508 -0.074983 +-0.311601 0.083779 -0.009789 +-0.314357 0.096724 -0.02922 +-0.32229 0.110319 -0.053009 +0.184241 -0.076208 -0.687737 +-0.083623 0.018781 -0.434761 +0.183078 -0.041209 0.287631 +0.077348 -0.10364 0.128523 +0.094965 -0.097446 0.127334 +0.10717 -0.101699 0.110056 +0.05856 0.088006 0.689861 +0.051062 0.096194 0.687617 +0.348306 0.101164 -0.105268 +0.347888 0.106007 -0.114837 +0.355884 0.113699 -0.120724 +0.009858 0.094499 0.586661 +0.108833 -0.085105 -0.914594 +0.10845 -0.099236 -0.923753 +0.109957 -0.097255 -0.892333 +0.111542 -0.104529 -0.851505 +0.097953 -0.099412 -0.47666 +0.111357 -0.101905 -0.477447 +0.119589 -0.112055 -0.499903 +0.059946 0.08543 0.699312 +-0.170476 -0.015066 0.311747 +-0.174529 -0.003813 0.314673 +-0.185116 0.005111 0.293771 +-0.181873 0.00546 0.318677 +-0.191732 0.011776 0.323338 +-0.179971 0.005966 0.340514 +-0.179228 0.004671 0.361014 +-0.19039 0.007652 0.367844 +0.072639 -0.099523 -0.614258 +0.0775 -0.093825 -0.66106 +0.071185 -0.075868 -0.660395 +0.084233 -0.088981 -0.709949 +0.096406 -0.099648 -0.754415 +0.107078 -0.108411 -0.800435 +0.26745 -0.00378 0.255424 +0.267637 -0.016481 0.253101 +0.260933 -0.028107 0.26999 +0.261114 -0.004045 0.273784 +0.25364 -0.003772 0.29392 +0.256818 -0.015226 0.29385 +0.252192 -0.024433 0.317098 +0.256449 -0.026587 0.292379 +0.252545 -0.037425 0.289561 +-0.148709 -0.127292 -0.700346 +-0.138585 -0.133981 -0.656673 +-0.153642 -0.128166 -0.654397 +-0.125176 -0.13535 -0.659367 +-0.166046 -0.1181 -0.650557 +-0.176239 -0.101699 -0.647905 +-0.171584 -0.108097 -0.692875 +-0.172985 -0.098496 -0.737968 +-0.165794 -0.110124 -0.73954 +-0.155324 -0.119252 -0.743172 +-0.143786 -0.124846 -0.74542 +-0.131961 -0.12693 -0.748294 +-0.12239 -0.126032 -0.749386 +-0.123473 -0.130744 -0.705913 +-0.111165 -0.132142 -0.659956 +0.160924 -0.021151 0.346205 +0.163994 -0.010665 0.349294 +0.16207 -0.006504 0.36247 +0.170454 -0.001635 0.353872 +0.179108 0.004632 0.361041 +0.175953 0.003835 0.375187 +0.19027 0.007613 0.367871 +0.198506 0.009604 0.379501 +0.060439 -0.06298 -0.613617 +0.068768 -0.059883 -0.659006 +0.070615 -0.043766 -0.658497 +0.353396 0.053089 -0.127413 +0.241181 -0.04512 0.336466 +0.245234 -0.033866 0.339391 +0.23613 -0.044764 0.363403 +0.245631 -0.02165 0.340974 +0.242331 -0.009756 0.341047 +0.2369 -0.020241 0.36773 +0.225764 -0.019446 0.394917 +0.228079 -0.032727 0.395573 +0.220946 -0.043937 0.422273 +0.227706 -0.045034 0.392829 +0.221642 -0.057862 0.388736 +0.207441 -0.072475 0.413589 +0.212263 -0.068618 0.384727 +0.201974 -0.072895 0.375283 +0.219409 -0.064794 0.355389 +0.223978 -0.060709 0.3278 +0.233838 -0.054393 0.332461 +0.244235 -0.046525 0.311355 +-0.246344 -0.046865 0.248314 +-0.246726 -0.046279 0.264919 +-0.23634 -0.052093 0.281321 +-0.245836 -0.04601 0.28581 +-0.244354 -0.046486 0.311327 +-0.252664 -0.037387 0.289534 +-0.256568 -0.026548 0.292352 +-0.235924 -0.050107 0.243671 +-0.224655 -0.049757 0.239529 +-0.225627 -0.052112 0.255722 +-0.2142 -0.053688 0.272322 +-0.22566 -0.054685 0.276767 +-0.224999 -0.058884 0.302177 +0.172787 -0.090507 -0.947133 +0.172491 -0.071786 -0.943035 +0.174601 -0.062859 -0.959185 +0.171268 -0.051249 -0.941831 +0.176039 -0.031099 -0.943277 +0.183272 -0.024297 -0.958462 +-0.182568 -0.083839 -0.644914 +-0.183173 -0.076213 -0.687764 +-0.179451 -0.07217 -0.735796 +-0.178245 -0.084473 -0.73585 +-0.172324 -0.091111 -0.788981 +-0.165041 -0.06852 0.372711 +-0.165792 -0.076618 0.390739 +-0.182487 -0.078599 0.387542 +-0.202092 -0.072856 0.375256 +-0.192214 -0.07212 0.365583 +-0.198164 -0.066806 0.340792 +-0.181422 -0.0669 0.357366 +-0.169688 -0.061476 0.354624 +-0.175191 -0.05662 0.334131 +-0.190446 0.003531 0.269551 +-0.197274 0.012155 0.273275 +-0.20633 0.017395 0.277484 +-0.216569 0.019145 0.281759 +-0.204031 0.016668 0.302641 +-0.203217 0.014564 0.328234 +-0.170079 -0.027283 0.310164 +-0.176718 -0.017823 0.287748 +-0.185292 -0.020352 0.264702 +-0.186102 -0.008149 0.266454 +0.363363 0.07473 -0.150603 +-0.107552 0.038514 -0.397668 +-0.177325 -0.059035 -0.736742 +-0.173896 -0.068383 -0.788751 +0.360636 0.075305 -0.160142 +-0.189058 0.010659 0.403907 +-0.198626 0.009643 0.379475 +-0.202387 0.007865 0.396803 +-0.212762 0.002657 0.389941 +-0.206169 -0.001395 0.41268 +-0.213588 -0.012012 0.413809 +-0.205328 -0.008308 0.434608 +-0.195397 0.000483 0.451738 +-0.18388 0.006758 0.473118 +-0.187941 -0.003168 0.494719 +0.062872 0.064146 0.147546 +0.159288 0.119897 -0.961836 +0.119568 0.115217 -0.964728 +0.343388 0.055728 -0.117661 +0.347914 0.053336 -0.139892 +0.341641 0.052257 -0.129927 +0.336395 0.047978 -0.120921 +0.321588 0.03046 -0.058404 +-0.03278 -0.126524 0.032896 +0.123153 -0.030496 -0.92279 +-0.219528 -0.064755 0.355362 +0.17326 -0.039216 0.310118 +0.165356 -0.036284 0.329203 +0.160346 -0.043597 0.347437 +0.160629 -0.032842 0.345031 +0.155372 -0.028368 0.356235 +0.150337 -0.035624 0.365792 +0.150665 -0.022157 0.367637 +0.154294 -0.010779 0.370781 +0.164253 -0.002173 0.375198 +0.035841 0.029887 0.534583 +0.079019 0.149079 0.400081 +0.349098 0.041786 -0.144112 +0.356004 0.042531 -0.154123 +-0.099083 -0.124611 -0.660924 +-0.113162 -0.120096 -0.750675 +-0.104916 -0.111415 -0.751447 +-0.102164 -0.117278 -0.708171 +-0.086684 -0.110071 -0.660427 +0.084782 0.150209 0.412004 +0.089822 0.149412 0.422 +0.097534 0.153991 0.414529 +0.212494 -0.063496 0.322903 +0.108072 -0.017674 -0.704252 +0.085284 0.006734 0.70766 +0.013054 -0.070632 0.772858 +-0.359252 0.073551 -0.158881 +0.35586 0.115447 -0.131675 +0.347078 0.121777 -0.131765 +0.11799 -0.014622 -0.948338 +0.118626 -0.022811 -0.937492 +0.077762 0.003004 0.692549 +0.149496 0.118045 -0.971237 +0.353272 0.046773 -0.161503 +-0.169398 -0.075532 -0.842498 +-0.166691 -0.077802 -0.88187 +-0.169032 -0.085956 -0.842673 +0.17116 -0.100231 -0.965211 +0.078337 0.029774 0.697426 +0.078949 0.032083 0.70963 +0.078269 0.042027 0.723896 +0.081211 0.025526 0.724481 +0.081294 0.020966 0.742447 +0.081482 0.008499 0.724226 +0.079611 -0.010056 0.725374 +0.078703 -2e-05 0.707557 +-0.212613 -0.063458 0.322876 +-0.200714 -0.062423 0.31826 +-0.201639 -0.056854 0.293115 +-0.189474 -0.05766 0.314342 +-0.18008 -0.049671 0.311534 +0.009779 -0.013603 0.811873 +-0.348227 0.066029 -0.138773 +-0.188471 -0.031806 0.264632 +-0.192304 -0.023168 0.243687 +-0.199008 -0.02674 0.227711 +0.195543 0.005391 -0.970995 +-0.001704 -0.064566 0.188111 +0.123955 0.115992 0.451438 +0.130797 0.094938 0.458999 +-0.244484 0.024749 0.252048 +-0.229914 0.023071 0.265249 +-0.22803 0.018149 0.286204 +0.181303 -0.06694 0.357393 +0.198046 -0.066846 0.34082 +0.192095 -0.072159 0.365611 +0.200594 -0.062462 0.318287 +0.182369 -0.078638 0.387568 +0.170041 -0.086435 0.410617 +0.165672 -0.076658 0.390767 +0.150865 -0.068843 0.390208 +0.164921 -0.06856 0.372738 +0.169569 -0.061516 0.35465 +0.163363 -0.053478 0.350949 +0.175072 -0.056658 0.334159 +0.179961 -0.04971 0.311561 +0.189355 -0.0577 0.314369 +0.135647 0.09831 -0.976539 +0.141277 0.10969 -0.974611 +0.126106 0.115455 -0.971237 +0.109014 0.105489 -0.967742 +0.224509 0.002044 0.362953 +0.23563 0.000737 0.339605 +0.245713 -0.001086 0.317242 +-0.233957 -0.054354 0.332434 +-0.224097 -0.06067 0.327773 +0.144342 -0.12144 -0.974756 +0.154109 -0.119068 -0.973887 +0.154443 -0.109248 -0.978226 +0.163545 -0.107182 -0.975334 +0.170636 -0.091241 -0.975334 +0.119337 0.007039 0.530859 +0.178109 -0.018702 0.526682 +0.164945 -0.013349 0.526722 +0.162346 0.000925 0.509971 +0.151118 -0.002354 0.520824 +0.134781 0.020876 0.511031 +0.132609 0.005697 0.523097 +-0.306945 0.042229 0.09751 +-0.303168 0.051644 0.074662 +-0.291433 0.05709 0.098532 +-0.296602 0.06151 0.059517 +-0.284876 0.064238 0.058154 +-0.303535 0.067031 0.025178 +-0.310169 0.071885 0.004791 +-0.313972 0.059722 0.00451 +-0.316999 0.047501 -0.000922 +-0.304492 0.018549 0.147779 +-0.306017 0.030089 0.12688 +-0.299644 0.043425 0.128327 +-0.290972 0.046839 0.150848 +-0.289846 0.052813 0.128654 +-0.288193 0.024543 0.197143 +-0.290501 0.036632 0.173892 +-0.297647 0.021679 0.172269 +-0.278244 0.048375 0.172783 +-0.275074 0.025804 0.220254 +-0.260374 0.027071 0.23891 +-0.262818 0.037546 0.219144 +-0.263681 0.048027 0.194925 +0.212654 0.002962 0.390454 +0.220072 -0.007654 0.391584 +0.213481 -0.011706 0.414323 +0.119387 -0.116328 0.050137 +0.110117 -0.123127 0.032005 +0.084289 -0.133045 0.031941 +0.097998 -0.127538 0.015318 +0.09455 0.144916 0.431457 +-0.055893 0.052902 0.614078 +-0.050027 0.052827 0.603861 +0.188938 0.01062 0.403933 +0.202273 0.007998 0.397072 +0.206061 -0.00109 0.413194 +-0.085415 0.006678 0.707696 +-0.086296 0.000176 0.696959 +-0.183198 -0.04117 0.287604 +-0.173379 -0.039177 0.310091 +0.205214 -0.008175 0.434878 +0.195277 0.000444 0.451765 +0.207934 -0.017726 0.451241 +0.209911 -0.026981 0.471619 +0.214345 -0.036924 0.448785 +0.213816 -0.055074 0.444661 +0.025324 -0.058631 0.7826 +0.01253 -0.061062 0.78584 +0.011407 -0.050197 0.794688 +0.206377 -0.070783 0.44007 +0.192742 -0.084742 0.435948 +0.180747 -0.093837 0.455606 +0.175672 -0.091547 0.432608 +0.157013 -0.090462 0.43041 +0.355823 0.129013 -0.15251 +0.167365 0.004612 0.390041 +0.154985 0.004237 0.403646 +0.172113 0.009406 0.407158 +0.174958 0.013221 0.428688 +-0.202617 0.011735 0.352242 +-0.359857 0.065634 -0.137145 +-0.358469 0.06381 -0.148531 +0.339127 0.109041 -0.111258 +-0.355676 0.07573 -0.135227 +-0.361979 0.072975 -0.149342 +0.100875 0.083121 -0.970754 +0.103018 0.096223 -0.970272 +0.115575 0.098697 -0.974129 +0.124166 0.086266 -0.97678 +0.131507 0.068883 -0.977745 +0.111941 0.077113 -0.975093 +0.208017 -0.065919 0.46385 +-0.078302 0.029706 0.697437 +0.058903 -0.072872 -0.54404 +0.057457 -0.067087 -0.57373 +0.063343 -0.085673 -0.573707 +0.055864 -0.050678 -0.574756 +0.073186 -0.104666 -0.575196 +0.086227 -0.121112 -0.577144 +0.078229 -0.107652 -0.546591 +0.084535 -0.107544 -0.520092 +0.072452 -0.093879 -0.518952 +0.062968 -0.078092 -0.517891 +0.057687 -0.062526 -0.518367 +0.058442 -0.052134 -0.497397 +0.056341 -0.045931 -0.519415 +0.055964 -0.039868 -0.546115 +0.058107 -0.034764 -0.575973 +-0.245354 -0.033827 0.339365 +-0.2413 -0.045081 0.336438 +0.341625 0.09724 -0.100812 +0.129441 -0.119864 -0.525052 +0.139869 -0.126175 -0.553229 +0.143561 -0.113256 -0.525748 +0.134261 -0.135387 -0.581649 +0.149939 -0.128617 -0.58101 +0.163143 -0.115538 -0.579851 +0.173515 -0.097078 -0.578243 +0.165684 -0.101575 -0.551639 +0.165725 -0.086894 -0.52567 +0.156171 -0.101734 -0.525316 +0.113951 -0.122268 -0.523613 +0.099083 -0.11872 -0.522086 +0.107925 -0.129584 -0.550956 +0.101374 -0.131867 -0.579516 +0.117808 -0.136392 -0.580816 +0.141849 0.001235 0.469012 +0.15723 0.009821 0.447299 +0.17877 0.011883 0.450648 +0.18376 0.006719 0.473145 +-0.06438 0.023247 0.636874 +-0.063112 0.03358 0.628538 +-0.205128 -0.037064 0.23151 +-0.204654 -0.043252 0.248828 +-0.214134 -0.045326 0.236279 +-0.194701 -0.041565 0.265974 +-0.203749 -0.049259 0.268678 +-0.221296 -0.04775 0.22812 +-0.216327 -0.041007 0.217945 +-0.211949 -0.032031 0.203072 +-0.2062 -0.033706 0.219456 +0.176307 -0.053861 -0.970754 +0.174371 -0.07259 -0.973405 +0.166037 -0.0826 -0.978226 +0.1554 -0.093877 -0.979672 +0.156574 -0.072029 -0.977504 +0.159115 -0.051027 -0.974611 +0.173162 -0.027178 -0.973164 +0.170514 -0.042869 -0.972683 +0.180207 -0.035449 -0.968585 +0.186921 -0.015395 -0.969067 +-0.074434 -0.029724 -0.656313 +-0.086129 -0.032795 -0.703283 +-0.091303 -0.048692 -0.74926 +-0.088386 -0.060223 -0.749972 +-0.097276 -0.061724 -0.795373 +-0.19108 -0.014562 0.512553 +0.091317 -0.106014 -0.49759 +0.172606 0.006961 0.492825 +0.153428 0.00626 0.488919 +0.145335 0.048119 0.463165 +0.141073 0.041311 0.48148 +0.128945 0.062822 0.483894 +0.131145 0.042934 0.49661 +0.190494 -0.014589 0.511124 +0.201084 -0.01873 0.493017 +0.187589 -0.0032 0.494019 +0.181753 -0.041215 -0.578804 +0.177671 -0.025978 -0.580003 +0.182778 -0.057623 -0.577679 +0.185387 -0.052529 -0.608923 +0.18629 -0.064883 -0.643856 +0.184542 -0.048934 -0.6447 +0.179789 -0.033866 -0.646825 +0.170847 -0.021688 -0.649466 +0.176552 -0.022224 -0.612247 +0.169293 -0.012787 -0.580641 +0.155768 -0.003262 -0.581065 +0.172059 -0.068744 -0.526163 +0.178575 -0.063213 -0.551391 +0.180073 -0.076866 -0.577502 +-0.360641 0.10223 -0.157401 +-0.362883 0.096396 -0.150932 +0.183637 -0.083834 -0.644886 +0.18003 -0.090725 -0.609774 +0.149608 -0.004021 -0.614175 +0.33473 0.104244 -0.099942 +0.064528 -0.030949 -0.614326 +0.074879 -0.029935 -0.657674 +0.061988 -0.042839 -0.478018 +0.065538 -0.034823 -0.462206 +0.095934 -0.011351 -0.657415 +0.08391 -0.018958 -0.65811 +-0.362464 0.103486 -0.146037 +0.362126 0.105398 -0.155069 +-0.096585 -0.099221 -0.751664 +-0.091146 -0.087026 -0.751883 +-0.084203 -0.088625 -0.70766 +-0.07074 -0.075656 -0.659033 +-0.077055 -0.093614 -0.659699 +-0.071779 -0.099456 -0.613822 +-0.088308 -0.072542 -0.75043 +0.008712 0.092262 0.790131 +0.025323 0.069293 0.800772 +0.017018 0.080849 0.797134 +0.024425 0.08945 0.787811 +0.039578 0.084327 0.7826 +0.0407 0.065028 0.794688 +0.181339 -0.009278 -0.973165 +0.170214 -0.002734 -0.973887 +0.15423 0.009263 -0.972683 +0.010846 -0.039475 0.801916 +0.010059 -0.027181 0.807399 +0.019509 -0.009221 0.810943 +-0.189676 -0.02732 0.526138 +0.157428 -0.013009 -0.65097 +0.142149 -0.008196 -0.653256 +0.126057 -0.006072 -0.654604 +0.111627 -0.007039 -0.656616 +-0.168201 -0.079358 -0.908568 +0.153661 -0.045747 0.361072 +0.021957 -0.035043 0.800794 +-0.245751 -0.02161 0.340947 +-0.252311 -0.024394 0.317072 +-0.256938 -0.015188 0.293823 +0.315719 0.098469 -0.030418 +0.323652 0.112064 -0.054205 +0.069311 -0.035047 -0.448088 +-0.225878 -0.01958 0.394647 +-0.23702 -0.020202 0.367703 +-0.242451 -0.009717 0.34102 +-0.06082 0.050615 0.625212 +-0.238481 0.01372 0.289848 +-0.22739 0.014637 0.311703 +-0.226356 0.008767 0.336769 +-0.215117 0.013529 0.332851 +-0.23575 0.000777 0.339577 +-0.245832 -0.001047 0.317215 +-0.247528 0.006025 0.292552 +-0.253759 -0.003734 0.293895 +-0.261233 -0.004006 0.273759 +-0.22018 -0.00796 0.391069 +-0.224623 0.001911 0.362682 +0.010481 0.086772 0.577044 +0.143699 -0.044555 0.377004 +-0.251061 0.011926 0.271567 +-0.070169 -0.043554 -0.657136 +-0.068323 -0.059671 -0.657643 +-0.078948 -0.058156 -0.70482 +0.138631 -0.069856 0.409434 +0.140603 -0.056375 0.391026 +0.144295 0.068666 0.390136 +0.131039 0.072281 0.373987 +0.119545 0.061258 0.365376 +0.148392 0.060229 0.409178 +0.14791 0.06047 0.427255 +0.061194 -0.101996 0.128584 +0.068223 -0.115659 0.112662 +0.055213 -0.122019 0.097083 +0.078208 -0.122298 0.098267 +0.090109 -0.125325 0.083517 +0.099539 -0.114531 0.09685 +0.119083 -0.100139 0.094385 +0.130689 -0.095004 0.080497 +0.133536 -0.081117 0.092084 +0.14342 -0.057279 0.089751 +0.079745 -0.131592 0.067127 +0.103922 -0.122391 0.067669 +0.055395 -0.131208 0.065896 +0.069381 -0.135449 0.049532 +0.124957 -0.10646 0.067114 +0.036158 -0.114652 0.094315 +0.042764 -0.124662 0.081103 +0.033238 -0.101133 0.108597 +0.045264 -0.094838 0.127286 +0.206129 -0.058717 0.486915 +0.197849 -0.077068 0.482803 +0.184214 -0.091027 0.478681 +0.140409 -0.081699 0.429785 +0.127904 0.080476 0.470882 +0.137787 0.071317 0.459794 +0.145259 0.063363 0.446056 +-0.26065 0.006429 0.260385 +0.119403 -0.006611 -0.956534 +-0.25518 0.018034 0.254722 +0.01688 -0.065543 0.196797 +0.03084 -0.067719 0.230546 +0.110934 -0.109238 -0.937974 +0.116602 -0.118716 -0.954292 +0.038681 -0.056487 0.774999 +0.036323 -0.044192 0.787834 +0.046088 -0.027895 0.790202 +0.034078 -0.03147 0.797181 +0.031518 -0.017656 0.804089 +0.142881 -0.10762 -0.979672 +0.073673 0.057384 0.724556 +0.074239 0.055939 0.737636 +0.133232 0.051032 -0.977021 +0.332002 0.116825 -0.076179 +0.059894 0.044442 0.78584 +0.051924 0.056737 0.789578 +0.051946 0.0401 0.794742 +0.047547 0.071461 0.78584 +0.047029 0.020604 0.800903 +0.039958 0.001019 0.804143 +0.050712 0.005504 0.797234 +0.054282 -0.009597 0.790202 +0.060455 0.00956 0.787834 +0.069883 0.012992 0.774999 +0.06472 0.029003 0.7826 +0.065112 0.048174 0.775876 +0.070107 0.051334 0.763795 +0.061801 0.065488 0.769652 +0.05546 0.081657 0.7638 +0.052205 0.076479 0.775876 +0.057184 0.084832 0.751847 +0.029857 -0.004128 0.808754 +-0.210827 -0.020354 0.184299 +-0.213135 -0.008264 0.161048 +0.125472 -0.004017 -0.963041 +0.077122 0.035307 0.756062 +0.07301 0.053779 0.751837 +0.066428 0.071108 0.745741 +0.074148 -0.002592 0.762786 +0.077291 0.016137 0.760543 +-0.240427 -0.009453 0.11697 +-0.226254 -0.009526 0.137936 +0.07628 -0.016315 0.745837 +-0.108726 0.077671 -0.08699 +-0.147896 0.060634 -0.08722 +-0.130339 0.072406 -0.087051 +-0.120429 0.074217 -0.051083 +-0.245918 0.004612 0.0982 +0.118984 -0.121476 -0.966898 +0.130897 -0.125393 -0.971237 +0.135425 -0.118096 -0.97678 +0.13368 0.025991 -0.973647 +0.136093 -0.000389 -0.968586 +0.207495 -0.037927 0.490559 +0.051363 -0.053485 0.762786 +0.071824 0.052174 -0.311375 +-0.109144 0.070674 -0.018124 +-0.068987 -0.135429 0.049518 +-0.079129 -0.131615 0.067054 +-0.102803 -0.122492 0.067471 +-0.057114 -0.134223 0.032887 +-0.044072 -0.130639 0.018188 +0.058097 -0.024607 0.778987 +0.068536 -0.021033 0.764406 +0.061016 -0.037903 0.764406 diff --git a/src/cython/example/ex_clustering.py b/src/cython/example/ex_clustering.py index bee82414..44b3d610 100644 --- a/src/cython/example/ex_clustering.py +++ b/src/cython/example/ex_clustering.py @@ -6,7 +6,7 @@ import sys sys.path.append("../sktda/") from clustering import * -X = np.loadtxt("human") +X = np.loadtxt("../../../data/points/human") print("Mapper computation with point cloud") mapper = MapperComplex(inp="point cloud", -- cgit v1.2.3 From f247f597baa4bf6ca2cd7371de73bb14e130d74e Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Wed, 19 Jun 2019 12:29:04 -0400 Subject: removed clustering files --- data/points/human | 4706 ----------------------------------- src/cython/example/ex_clustering.py | 64 - src/cython/sktda/clustering.py | 269 -- 3 files changed, 5039 deletions(-) delete mode 100644 data/points/human delete mode 100644 src/cython/example/ex_clustering.py delete mode 100644 src/cython/sktda/clustering.py diff --git a/data/points/human b/data/points/human deleted file mode 100644 index feaa0dd1..00000000 --- a/data/points/human +++ /dev/null @@ -1,4706 +0,0 @@ -0.144529 -0.014328 0.381773 --0.15378 -0.045707 0.361045 --0.153917 -0.057275 0.371308 --0.172725 0.007 0.492798 --0.119664 0.061296 0.365349 -0.128166 -0.068572 0.432762 -0.089198 0.014048 -0.447694 -0.075569 0.021145 -0.417112 - 0.098403 0.03188 -0.416332 --0.135518 -0.031058 -0.793657 --0.155207 -0.043962 -0.867084 --0.155561 -0.039537 -0.782486 -0.047747 0.043681 0.604458 -0.01706 0.058681 0.583267 -0.153119 -0.065476 -0.465974 -0.134237 -0.06509 -0.429525 -0.138009 -0.083138 -0.463111 -0.061936 0.021354 -0.365004 --0.327282 0.105737 -0.088733 --0.333168 0.111481 -0.099092 -0.02915 0.038215 0.809093 -0.000855 0.039818 0.813241 -0.188052 0.020848 -0.942916 -0.200327 0.058375 -0.947736 -0.170213 0.052306 -0.931828 --0.051119 -0.110776 0.111316 --0.024531 -0.111686 0.078082 --0.021008 -0.089762 0.10657 -0.158999 0.015347 -0.916402 -0.127084 0.053037 -0.930089 -0.110535 0.0176 -0.450151 -0.12031 0.025078 -0.419837 -0.125486 0.007107 -0.444987 -0.087186 -0.068787 -0.428022 -0.068096 -0.048487 -0.423994 -0.074575 -0.071291 -0.460151 --0.129151 -0.031845 0.547791 --0.092828 -0.001702 0.544926 --0.146897 -0.021057 0.536567 --0.040908 -0.080471 0.144988 --0.070348 -0.092228 0.142231 -0.135184 -0.05792 -0.366185 -0.150502 -0.030301 -0.366514 -0.154969 -0.053619 -0.297939 --0.046419 0.016608 0.551426 --0.140395 -0.04129 0.102368 --0.15361 -0.023507 0.066896 --0.143445 -0.07334 0.071094 --0.150698 -0.083179 0.012934 --0.164552 -0.040183 0.016637 --0.172922 -0.056311 -0.054118 -0.142078 0.114992 0.400106 -0.136581 0.121451 0.422073 -0.118363 -0.052599 -0.969187 -0.110299 -0.090045 -0.973743 -0.106227 -0.067834 -0.951593 --0.075572 -0.055159 0.227875 --0.048656 -0.08199 0.316154 --0.088566 -0.065864 0.297905 --0.175443 0.052256 -0.931828 --0.142719 0.087605 -0.938697 --0.186188 0.080344 -0.942939 --0.281944 -0.02183 0.181925 --0.282953 -0.017408 0.129994 --0.25389 -0.034093 0.168434 --0.092047 -0.041549 0.557254 --0.085291 -0.062035 0.558917 --0.302715 0.009876 0.086968 --0.282908 -0.00097 0.077127 --0.117228 0.023298 0.342619 --0.177404 -0.012747 -0.051392 --0.272247 -0.020141 0.233158 --0.254475 -0.039985 0.216094 --0.017864 0.070352 0.178973 --0.003695 0.067376 0.163481 -0.030308 0.031315 0.603728 -0.02661 0.115199 0.744608 -0.033244 0.11394 0.710348 -0.012279 0.123046 0.708533 --0.05487 -0.030769 0.660855 --0.04444 -0.039595 0.603866 -0.027038 -0.082087 0.737178 -0.023403 -0.083864 0.708068 -0.044858 -0.07186 0.710449 --0.141696 0.005473 0.082272 --0.322537 0.044383 -0.093982 -0.137584 0.087655 -0.938697 -0.110404 0.089056 -0.947375 -0.201593 0.059307 -0.975093 -0.19232 0.094332 -0.975768 -0.209811 0.077354 -0.962319 -0.008617 -0.099163 0.047708 -0.005395 -0.079622 0.076357 -0.106371 0.041851 -0.959546 -0.116565 0.024136 -0.939902 -0.10789 0.05867 -0.947567 -0.117802 0.064238 0.011831 -0.099654 0.053985 0.060307 -0.12682 0.046382 0.064253 -0.14171 0.005468 0.082276 -0.162647 0.005991 0.019937 -0.146819 0.043902 0.018713 --0.086735 -0.081196 0.550877 --0.142522 -0.064292 0.541934 --0.101551 -0.092019 0.536874 --0.147816 -0.090727 -0.053942 -0.089669 -0.075237 -0.296805 -0.110958 -0.094262 -0.219465 -0.067718 -0.086145 -0.213647 -0.035525 -0.10073 0.457763 -0.000152 -0.097146 0.473706 -0.000252 -0.080555 0.318839 -0.16693 0.014297 -0.292232 -0.165909 0.033768 -0.213443 -0.177974 -0.006912 -0.216479 -0.122756 0.049674 -0.332788 -0.11496 0.064838 -0.287502 -0.148695 0.045817 -0.290571 -0.055746 -0.030683 0.660961 -0.033546 -0.066936 0.676952 -0.017439 -0.069226 0.625575 -0.039048 0.038642 -0.121677 -0.035134 0.012056 -0.055409 -0.054628 0.051615 -0.046746 --0.219382 0.038108 0.176265 --0.218586 0.03396 0.225697 --0.200001 0.012939 0.213512 -0.134152 -0.008037 -0.466849 --0.111507 0.041802 -0.959546 --0.105326 0.074882 -0.964246 --0.113025 0.05862 -0.947567 --0.054482 -0.034932 0.574847 --0.090128 -0.021415 0.553574 --0.116313 -0.048454 0.13681 --0.09038 -0.053234 0.172229 --0.105186 -0.014928 0.166671 --0.126942 0.046414 0.064231 --0.116088 0.034063 0.10285 -0.078883 0.038902 -0.375952 -0.08771 0.046925 -0.353531 -0.166615 -0.02867 -0.506862 -0.15046 -0.019487 -0.47215 -0.156742 -0.040331 -0.46813 -0.049615 0.064657 0.606895 --0.093081 0.017267 0.197077 --0.107564 -0.001066 0.279921 --0.087365 0.043211 0.25934 --0.229315 0.033998 0.125778 --0.248089 0.054546 0.138866 -0.106254 -0.066109 -0.3667 -0.125371 -0.075616 -0.30084 --0.035641 -0.100747 0.457937 --0.095086 -0.103368 0.460305 -0.018598 0.108161 0.632002 -0.008009 0.110019 0.630723 --0.145172 -0.126207 -0.962029 --0.147995 -0.115103 -0.901861 --0.165635 -0.112794 -0.963402 -0.181052 0.080394 -0.942939 --0.037409 0.016634 -0.022456 --0.054935 0.051562 -0.047076 --0.051074 0.0426 0.012895 -0.024454 -0.043608 -0.124411 -0.030596 -0.06583 -0.057455 -0.026133 -0.020196 -0.061267 -0.004091 -0.07642 0.016114 --0.143275 0.11508 -0.950869 --0.10902 0.06955 0.488685 --0.119427 0.028724 0.512181 --0.077684 0.050975 0.507111 -0.03316 -0.09173 0.54611 --0.126953 0.020019 -0.968705 --0.153991 -0.027525 -0.970453 --0.162328 0.037423 -0.975575 -0.019284 0.112865 0.690206 --0.017888 0.004075 0.813135 --0.036857 0.015351 0.806263 --0.019684 -0.022438 0.806286 --0.118067 0.060321 -0.975334 --0.11084 -0.089559 0.388201 --0.136103 -0.098275 0.477748 --0.068592 0.049409 0.668449 --0.354522 0.111955 -0.119529 -0.133555 0.0107 -0.91375 -0.152919 -0.021289 -0.903386 --0.047277 0.088388 0.477505 -0.028943 0.100045 0.614915 --0.178297 -0.027228 -0.973164 --0.195107 0.015542 -0.975093 --0.009711 -0.053847 -0.021292 --0.020989 -0.095217 -0.015669 --0.206728 0.059258 -0.975093 --0.160538 0.080674 -0.978019 --0.028046 0.017736 0.594311 --0.099769 0.054021 0.060283 -0.292938 0.024667 0.005698 -0.281872 0.042386 0.010061 -0.293815 0.043479 -0.024205 -0.253755 -0.042591 0.24146 -0.236374 -0.050375 0.26034 -0.2334 -0.049604 0.232122 --0.041908 -0.013527 0.798038 --0.169026 -0.017468 -0.554275 --0.180719 -0.036864 -0.610601 --0.177641 -0.046876 -0.552508 -0.142302 -0.064331 0.542099 -0.091978 -0.041556 0.557259 -0.085357 -0.061971 0.558866 -0.163846 -0.07001 -0.134881 -0.147918 -0.090765 -0.053915 -0.128677 -0.096438 -0.134035 -0.033866 0.08705 0.595856 --0.061852 0.08285 0.67803 --0.064405 0.0745 0.705228 --0.057315 0.087943 0.688282 -0.026825 0.058063 0.011471 -0.00959 0.046503 -0.022489 -7.5e-05 0.067016 0.012148 -0.037289 0.016594 -0.022429 -0.027248 0.032898 -0.019611 -0.051064 0.042566 0.012814 -0.172917 -0.056346 -0.054093 -0.177533 -0.01278 -0.051369 -0.165297 -0.040174 0.016676 --0.049827 -0.001275 0.628239 --0.046082 -0.007732 0.588234 -0.027282 0.017653 0.594293 -0.218467 0.03392 0.225725 -0.218303 0.022488 0.260748 -0.246836 0.034226 0.236039 -0.180761 -0.032705 -0.133588 --0.061259 -0.067857 -0.496632 --0.054909 -0.056275 -0.544951 --0.05886 -0.036687 -0.498712 --0.096128 0.07647 0.350771 --0.125425 -0.075572 -0.300885 --0.089749 -0.075321 -0.29683 --0.111609 -0.094226 -0.21961 --0.059916 -0.025146 -0.5475 --0.070274 -0.011925 -0.501404 --0.014861 0.085999 0.475193 -0.011944 0.087545 0.471872 --0.013265 0.101124 0.440331 --0.111865 -0.105085 -0.952919 --0.111362 -0.067884 -0.951593 --0.108817 -0.085906 -0.928478 -0.129068 -0.032432 0.547796 -0.092747 -0.004496 0.54493 --0.154642 0.003294 -0.352704 --0.142743 0.028986 -0.337671 --0.069935 -0.024902 -0.463284 --0.315481 0.074492 -0.075996 --0.325874 0.058528 -0.097514 --0.332121 0.079166 -0.097184 -0.065756 0.113544 0.355893 -0.099174 0.128438 0.362935 -0.091993 0.102855 0.353431 -0.150875 -0.083168 0.013016 --0.108399 -0.048104 0.306063 --0.047271 0.099741 0.356288 --0.066219 0.088165 0.321493 --0.035225 -0.092767 0.543027 --0.009859 0.104972 0.60128 --0.043422 -0.067033 0.234382 --0.197455 0.094282 -0.975768 --0.104462 -0.111554 -0.498426 --0.078262 -0.094503 -0.497102 --0.092157 -0.088636 -0.463068 --0.146801 -0.077454 -0.220163 -0.086804 -0.081061 0.550911 -0.134521 0.11988 0.378405 -0.118328 0.120392 0.364735 -0.115393 0.14537 0.390518 --0.054843 -0.015197 0.563407 --0.05504 0.025 0.792306 --0.070388 0.032156 0.769815 --0.063682 -0.006236 0.777538 --0.000626 0.114562 0.672841 -0.253835 -0.01734 0.119652 -0.223827 -0.021655 0.161215 -0.231867 0.004314 0.117834 --0.114756 0.089415 -0.973165 --0.127269 0.107695 -0.974322 -0.096751 0.074035 -0.05055 -0.078066 0.059374 0.009685 -0.000726 0.11622 0.656618 -0.165173 0.032563 -0.050176 -0.000689 -0.080625 0.101576 --0.005268 -0.079602 0.076344 -0.183917 -0.053008 0.530657 -0.17545 -0.080203 0.523558 -0.141167 -0.093629 0.517571 --0.051253 0.096483 0.711906 --0.022715 -0.04717 0.792329 --0.047913 -0.041404 0.777538 -0.052291 0.096624 0.712047 --0.026424 -0.020287 -0.061717 --0.035397 0.011988 -0.055788 --0.024475 -0.015906 -0.03417 --0.062564 0.085644 0.653961 -0.006432 -0.067984 0.669362 --0.091524 -0.120719 -0.548864 --0.352035 0.051345 -0.126219 --0.078178 0.050942 0.154885 --0.07227 0.047244 0.203935 --0.048281 0.062448 0.195881 -0.155341 0.052205 -0.127805 -0.177243 0.012794 -0.130554 -0.072937 0.07156 -0.124618 -0.118217 0.146923 0.421907 -0.020198 0.076819 0.079276 -0.139978 0.062527 -0.211926 --0.089177 -0.111241 0.111846 --0.09518 -0.081905 0.14393 --0.074495 -0.071296 -0.460179 --0.042869 0.071572 0.14139 -0.00073 -0.05162 -0.010499 --0.004172 -0.07642 0.016012 --0.131034 -0.082145 0.442399 --0.100523 -0.027685 0.22648 --0.015731 -0.07176 0.615875 -0.119466 0.075689 -0.126799 --0.132503 -0.106466 -0.501087 --0.122962 -0.130064 -0.552684 --0.074738 0.044114 0.706666 --0.066077 0.071903 0.732644 --0.062916 0.068521 0.757684 --0.048496 0.098136 0.740433 --0.033166 0.000252 -0.280952 --0.048184 0.017041 -0.328096 --0.048173 0.0348 -0.27966 --0.025707 -0.068824 0.768329 --0.166577 0.1103 -0.974177 --0.124674 -0.051241 -0.914051 --0.112361 -0.082599 -0.886027 --0.08053 -0.007338 -0.458902 --0.068099 -0.00941 -0.416012 --0.214946 0.077303 -0.962319 --0.205462 0.058324 -0.947736 -0.138907 0.065218 -0.050517 -0.199882 0.012901 0.213538 -0.200105 0.01313 0.252288 -0.055229 0.054786 -0.204024 --0.024533 0.091239 0.330841 --0.172277 -0.081982 -0.551109 --0.167673 -0.057628 -0.50493 --0.058987 -0.06452 -0.289445 --0.068156 -0.086411 -0.213966 -0.254357 -0.040025 0.216122 --0.067674 0.042176 -0.330429 --0.076952 0.060866 -0.28229 -0.013977 0.113422 0.640177 --6.9e-05 0.086527 0.3376 -0.019179 0.079744 0.281051 --0.015722 0.079249 0.274606 -0.115072 -0.070048 -0.42498 -0.077551 -0.065157 -0.362486 -0.033381 0.053515 0.505504 -0.054667 0.033265 0.526511 -0.077572 0.050943 0.507134 -0.086553 0.046191 0.265442 -0.059227 0.073277 0.277063 -0.088572 0.057052 0.304907 -0.057158 -0.046385 -0.353944 --0.056639 0.061106 0.779722 --0.043816 0.051377 0.797042 --0.031526 0.076752 0.792306 -0.171482 -0.044588 -0.21885 --0.143382 0.00154 -0.554016 --0.164175 -0.011574 -0.613396 -0.045344 -0.080279 -0.128946 --0.043257 0.090499 0.76982 --0.078577 0.001976 0.744204 --0.07502 -0.017208 0.703979 --0.068733 -0.033112 0.745947 --0.009464 0.029546 0.541617 --0.050437 -0.062276 0.744451 --0.078293 0.038525 0.740162 -0.024463 0.091212 0.330858 --0.112377 0.005125 -0.552117 --0.131688 -0.001292 -0.614531 --0.183022 -0.043595 -0.957618 --0.196997 -0.004429 -0.960993 -0.240756 0.018538 0.268716 -0.095305 0.074055 -0.207363 -0.056798 0.020951 0.627455 -0.062692 0.009616 0.652266 -0.049787 -0.001444 0.628412 -0.146665 -0.077493 -0.22013 --0.037163 -0.033757 -0.284242 --0.038849 -0.058215 -0.209167 --0.065469 -0.047111 -0.461306 -0.047178 0.099707 0.356312 -0.308841 0.028975 0.094589 -0.301247 0.005445 0.09387 -0.30888 0.029103 0.019447 -0.017844 0.071064 0.186113 --0.155734 -0.088774 -0.50245 --0.152652 -0.115873 -0.552632 -0.011229 0.02562 -0.035193 --5.3e-05 0.023896 -0.036713 --0.088766 0.057083 0.305007 --0.120964 0.004341 -0.951472 --0.115539 0.089006 -0.947375 -0.292644 0.072606 -0.014141 --0.123809 0.004777 -0.499938 -0.000277 -0.080877 0.691549 --0.034692 0.087566 0.596768 -0.103311 0.149984 0.391528 -0.078795 0.134336 0.365284 -0.135882 -0.098348 0.47797 -0.094456 -0.103522 0.46077 --0.134073 -0.008009 -0.466876 --0.142742 -0.028419 -0.439643 --0.106323 -0.066074 -0.366736 --0.080573 -0.002643 -0.550185 --0.099477 -0.002733 -0.614967 --0.329349 0.034688 -0.054928 --0.342692 0.069348 -0.059398 --0.347817 0.042787 -0.102173 -0.000763 0.044977 0.590746 -1.7e-05 0.025278 0.592935 --0.029687 0.031244 0.60365 --0.053206 0.088406 0.622436 -0.059948 -0.063445 -0.289438 --0.127417 -0.115851 -0.974611 --0.121738 -0.118767 -0.954292 --0.151626 -0.006646 -0.507398 --0.154614 -0.053562 -0.298047 --0.141938 -0.093481 0.516984 --0.165744 -0.028673 -0.506889 --0.133184 0.029585 -0.373943 --0.146871 0.043935 0.01869 --0.161897 0.006001 0.019931 -0.039669 -0.057032 -0.208807 --0.028046 0.038112 0.808937 --4.6e-05 -0.020914 -0.03562 --0.071917 0.145224 0.383713 --0.078914 0.134376 0.365257 --0.103429 0.150023 0.391501 --0.016033 0.0586 0.583186 --0.150342 -0.019487 -0.472176 --0.058188 -0.098083 -0.059091 --0.030917 -0.065889 -0.057781 --0.0438 -0.102119 -0.030128 --0.099293 0.128477 0.362907 --0.092113 0.102894 0.353403 --0.118447 0.120432 0.364707 --0.0642 0.142063 0.402239 -0.044609 -0.039585 0.604051 --0.168519 -0.098024 -0.977937 --0.179056 -0.08227 -0.963161 --0.122882 -0.110201 -0.896682 --0.156663 -0.040336 -0.468156 -0.107748 -0.105176 -0.056081 -0.1285 -0.039664 0.355589 --0.158194 -0.021338 -0.903386 --0.164797 0.015297 -0.916402 --0.176744 -0.016583 -0.931828 -0.033356 0.125884 0.406133 -0.010034 0.099979 0.392826 -0.01712 0.102705 0.44066 -0.024443 -0.015939 -0.034147 -0.009626 -0.053851 -0.021289 -0.042447 0.121595 0.37503 --0.347781 0.05466 -0.105101 --0.115513 0.145409 0.39049 -0.111316 -0.089478 0.388386 --0.128517 -0.00972 0.345704 -0.080848 0.011758 0.692945 --0.067863 -0.031032 -0.431189 --0.118337 0.146961 0.421881 -0.045869 -0.069312 0.571091 --0.126084 0.090514 0.363964 --0.356352 0.06532 -0.102173 --0.281744 0.042127 0.010238 --0.292665 0.041989 -0.023182 --0.2916 0.071244 -0.013206 --0.016178 0.060644 0.806268 --0.34744 0.048022 -0.151125 --0.114149 0.105438 -0.967742 --0.162121 -0.095588 0.45288 --0.114846 -0.090481 -0.461001 --0.087198 -0.068891 -0.428048 -0.042983 -0.102146 -0.030109 -0.023895 -0.066464 -0.037126 -0.045982 -0.007651 0.589677 --0.13249 -0.021343 -0.963114 --0.12112 -0.031941 -0.947976 --0.123497 -0.052648 -0.969187 --0.173337 -0.062024 -0.975816 --0.212735 0.034612 -0.964126 --0.13793 -0.083142 -0.463138 --0.032206 0.113799 0.710208 --0.018056 0.112723 0.690066 --0.153041 -0.06548 -0.466002 --0.084204 -0.118301 -0.016723 --0.107774 -0.105145 -0.056224 --0.357731 0.118728 -0.141076 --0.012173 0.100156 0.388899 --0.042563 0.12163 0.375005 --0.033484 0.125919 0.406108 -0.00895 0.029531 0.541612 -0.034096 0.103917 0.67485 -0.316842 0.076237 -0.077194 -0.323899 0.046128 -0.095179 -0.025035 -0.017397 -0.204731 -0.022904 -0.002982 -0.119922 --0.017702 0.108056 0.631897 --0.090769 0.050144 0.086125 --0.071768 0.049796 0.05205 -0.058343 0.098285 0.662255 -0.062548 0.085885 0.653903 -0.047412 0.103597 0.643011 --0.356011 0.072244 -0.110379 --0.094694 0.016232 0.53498 -0.031138 0.0202 -0.202722 -0.023257 0.108698 0.644556 -0.298563 0.049907 0.106045 -0.309127 0.055705 0.032301 --0.127245 0.125928 0.44046 --0.107979 0.133071 0.444265 -0.002793 0.120311 0.643807 --0.139345 0.01065 -0.91375 -0.191354 -0.010656 0.245307 -0.000195 -0.089014 0.717793 --0.022347 -0.083906 0.707302 --0.033776 0.053541 0.505486 -0.0076 0.108722 0.620553 -0.009016 0.106372 0.608066 -0.05338 0.099889 0.672242 -0.247319 0.050332 0.189783 -0.277263 0.037855 0.19703 -0.066826 0.030362 0.643759 -0.068832 0.018953 0.660837 --0.05508 0.054469 -0.2044 --0.095081 0.07404 -0.207566 --0.121146 -0.113881 0.007467 --0.007983 -0.067736 0.665032 --0.139878 0.062566 -0.211961 --0.148273 0.045873 -0.29067 --0.165897 0.033789 -0.213473 --0.041772 0.121934 0.43798 -0.109205 -0.048064 0.306801 --0.094235 0.003354 -0.498127 -0.277358 0.054616 0.14883 -0.247971 0.054507 0.138894 -0.15507 0.003229 -0.352581 -0.143146 0.028923 -0.337551 -0.349178 0.044531 -0.10337 --0.134886 -0.057869 -0.366286 -0.301726 0.003593 0.142683 -0.299445 0.033456 0.150221 -0.291556 0.007949 0.193786 --0.36146 0.095938 -0.122006 --0.292738 0.024324 0.005932 -0.215633 0.016904 0.307366 -0.333478 0.080898 -0.098369 --0.354136 0.088244 -0.126082 --0.024054 -0.018703 -0.205089 --0.030282 0.019018 -0.203081 --0.023349 -0.003622 -0.121357 --0.009637 0.122749 0.70621 -0.162028 -0.090011 -0.885108 -0.165776 -0.102924 -0.790685 -0.143895 -0.115061 -0.901856 -0.051724 0.088579 0.622321 -0.061724 0.063033 0.633357 -0.06924 0.049621 0.668534 -0.35772 0.067034 -0.103388 -0.214633 -0.048801 0.252022 -0.135277 -0.031016 -0.794097 -0.151255 -0.043483 -0.866807 -0.130025 -0.042014 -0.864241 --0.027974 0.006868 -0.029505 --0.024899 -0.044248 -0.125845 -0.142675 -0.028427 -0.439606 -0.146143 -0.047721 -0.430244 --0.344331 0.038782 -0.132348 --0.033038 0.103786 0.67472 --0.125521 0.007147 -0.445015 --0.110456 0.017639 -0.450178 --0.177355 0.012827 -0.130577 --0.177958 -0.006894 -0.216507 -0.020331 -0.09522 -0.015547 --0.047532 0.103303 0.641395 --0.028376 0.100234 0.614667 --0.134303 -0.065085 -0.42956 --0.354421 0.12716 -0.151317 --0.348795 0.121213 -0.142322 -0.092382 0.046406 0.119475 -0.116004 0.034072 0.102858 -0.090667 0.050145 0.086144 --0.13464 0.11992 0.378378 --0.145812 0.082925 0.387467 --0.142197 0.115031 0.400078 --0.073683 0.142378 0.425002 --0.11906 0.10213 0.463337 --0.137325 0.059317 0.473022 --0.216529 -0.057972 0.41774 --0.196262 -0.081978 0.459561 --0.211776 -0.046564 0.468374 -0.179611 -0.005892 0.290372 -0.072121 0.033447 0.670515 --0.114173 0.06488 -0.287538 -0.086455 -0.101061 0.513489 --0.148602 0.042639 0.439711 --0.122607 0.04972 -0.332836 --0.13875 0.086783 0.448968 --0.166519 0.014354 -0.292347 -0.089044 -0.065899 0.298075 -0.100498 -0.027722 0.226627 -0.048519 -0.014071 -0.352485 -0.038536 -0.03231 -0.284242 -0.000144 -0.076816 0.142161 --0.067966 -0.048732 -0.424016 -0.046995 0.088353 0.477529 -0.088208 0.107273 0.467101 -0.061381 0.118752 0.456477 -0.193747 0.011959 0.298081 -0.034539 0.001698 -0.280952 -0.048886 0.018356 -0.328229 --0.000763 0.058573 0.503145 --0.064384 0.02103 -0.364348 --0.079524 0.038923 -0.375843 --0.088095 0.046844 -0.35346 -0.279857 0.058875 0.019882 -0.256561 0.051897 0.087347 -0.293923 0.07157 0.020631 --0.025409 0.115069 0.74448 -0.001008 0.120894 0.74601 -0.024428 -0.118227 0.018318 -0.045667 -0.121959 -0.007542 -0.252255 0.033409 0.076695 --0.158045 -0.017034 0.358421 --0.15663 -0.037984 0.35548 --0.148633 0.077503 0.419924 --0.180872 -0.032671 -0.13361 --0.17169 -0.094525 0.495191 --0.196785 -0.069355 0.503 --0.136701 0.121491 0.422046 --0.088328 0.107312 0.467073 --0.356358 0.050663 -0.148806 --0.129249 -0.096403 -0.134181 --0.163998 -0.069974 -0.134907 --0.150151 -0.030244 -0.366624 -0.062655 -0.048958 0.705925 -0.081642 -0.002642 -0.550158 -0.095049 0.003325 -0.498101 -0.113446 0.005123 -0.552089 --0.17147 -0.044567 -0.218881 --0.356494 0.095415 -0.106381 --0.081569 -0.101578 -0.133396 -0.359248 0.088392 -0.100782 --0.046231 -0.080746 -0.130322 -0.041642 0.121894 0.438006 -0.129402 -0.009686 0.345732 -0.201901 -0.019149 0.207794 -0.209533 0.008252 0.16446 --0.329022 0.034296 -0.095499 --0.065874 0.113583 0.355867 -0.075559 -0.055198 0.227902 -0.061849 -0.069654 0.179403 -0.089743 -0.053273 0.172255 --0.115191 -0.070046 -0.425006 --0.115434 -0.090095 -0.973743 -0.278928 0.059992 0.097192 -0.113573 -0.062643 -0.871804 -0.101579 -0.052474 -0.79698 -0.124623 0.004748 -0.499911 -0.161939 -0.11913 -0.696457 -0.136289 -0.130094 -0.703771 -0.149847 -0.119757 -0.783024 -0.225358 -0.039917 0.205755 -0.071144 -0.011928 -0.501376 -0.080559 -0.007372 -0.458874 -0.344051 0.071092 -0.060589 --0.357919 0.086664 -0.099675 --0.061593 -0.049099 0.705784 --0.032553 -0.067034 0.676746 --0.077423 -0.065374 -0.362509 -0.105019 -0.014965 0.166697 -0.212039 -0.041769 0.226301 -0.113461 -0.126095 -0.709147 -0.12885 -0.123528 -0.796839 --0.047846 -0.015163 -0.352407 -0.119073 0.044154 -0.348785 -0.116024 -0.048486 0.136832 -0.345781 0.117308 -0.104241 -0.351121 0.109018 -0.094476 -0.052426 -0.056817 0.574477 -0.095396 -0.072379 -0.798043 --0.328432 0.095191 -0.091348 --0.061499 0.118792 0.456451 --0.056158 -0.047468 -0.353951 --0.069627 0.003538 -0.420243 --0.094278 0.054827 -0.332642 -0.31826 0.03996 -0.016181 --0.043757 -0.071989 0.710178 -0.105996 0.001263 0.27446 -0.076423 0.042651 0.208272 --0.116755 -0.036153 -0.785482 --0.134088 -0.042806 -0.864056 -0.253771 -0.034132 0.168461 -0.155978 -0.039528 -0.782461 -0.169688 -0.057425 -0.788607 --0.009594 0.046517 -0.022378 -0.152497 -0.006648 -0.507371 --0.342275 0.113004 -0.121218 --0.328432 0.10443 -0.05057 --0.333615 0.114459 -0.087733 --0.349759 0.107273 -0.09328 --0.087764 0.135617 0.442196 -0.162192 -0.06653 -0.871875 -0.174311 -0.078585 -0.788353 --0.089218 0.014086 -0.447721 -0.197414 -0.033855 0.245837 -0.094128 -0.081944 0.143956 --0.169532 -0.022453 -0.295043 -0.01708 0.100498 0.598854 -0.093526 0.014904 0.191271 -0.282834 -0.017447 0.13002 -0.282789 -0.00101 0.077153 -0.108091 -0.083728 -0.886243 -0.114925 -0.090476 -0.460974 -0.229196 0.03396 0.125804 --0.011346 0.025647 -0.035213 -0.062826 0.08297 0.678076 --0.172061 -0.002328 0.335921 --0.169846 -0.00047 0.366421 -0.09249 -0.103709 -0.710652 -0.112314 -0.116058 -0.788678 -0.281825 -0.02187 0.181952 -0.272129 -0.02018 0.233186 -0.269184 0.012064 0.241483 -0.262166 -0.016175 0.27233 --0.143576 -0.133746 -0.615782 --0.160869 -0.119135 -0.696484 --0.135325 -0.130063 -0.703567 -0.171942 -0.002366 0.335949 -0.190821 0.010089 0.346412 -0.080132 -0.074061 -0.708891 -0.100808 -0.096899 -0.79974 -0.357678 0.048422 -0.139516 --0.071777 0.023237 0.672706 -0.237792 -0.032614 0.366062 -0.228778 -0.055637 0.359697 -0.2494 -0.035982 0.31464 -0.219264 0.03807 0.176292 --0.25479 -0.03794 0.26771 --0.253873 -0.042551 0.241433 --0.236493 -0.050336 0.260313 --0.13773 -0.005931 -0.414142 --0.134495 0.011459 -0.41487 -0.163194 -0.058909 -0.917034 -0.17152 -0.016533 -0.931828 --0.169841 -0.110147 -0.611986 --0.178531 -0.092624 -0.689969 --0.153471 -0.080507 0.409463 --0.176946 -0.072394 0.374346 --0.193866 0.011998 0.298053 --0.17973 -0.005852 0.290346 --0.200223 0.013169 0.252261 --0.120376 0.02512 -0.419874 --0.183095 -0.070451 -0.608863 --0.182665 -0.061429 -0.687606 --0.193025 0.006998 0.426495 --0.146159 -0.047714 -0.430289 -0.050021 0.062413 0.195904 -0.352977 0.0731 -0.151044 -0.138139 0.11513 -0.950869 -0.330384 0.03604 -0.096696 -0.339748 0.038028 -0.111719 --0.008455 -0.099159 0.047585 -0.134852 -0.023383 -0.900373 --0.19131 -0.081309 0.407231 -0.33174 0.043482 -0.109801 -0.332326 0.053947 -0.107568 -0.164425 -0.024219 0.330085 -0.157925 -0.017074 0.358447 -0.25467 -0.037979 0.267736 -0.04634 0.016295 0.551532 -0.071798 0.145186 0.383741 --0.149476 -0.121491 -0.974756 --0.144727 -0.085602 -0.97833 --0.112139 -0.135913 -0.616767 --0.113224 -0.125811 -0.707323 -0.06408 0.142024 0.402264 -0.094829 0.15407 0.402267 -0.235265 -0.053833 0.306932 -0.357062 0.077449 -0.136492 -0.116128 -0.036368 -0.78768 -0.357733 0.052408 -0.149996 --0.112323 0.042538 -0.375643 --0.014254 0.018502 0.815081 --0.026157 -0.082194 0.736953 -0.000167 -0.084481 0.746748 -0.122329 -0.012356 -0.929538 -0.181078 0.111755 -0.961681 -0.348813 0.049767 -0.152316 --0.098522 0.031918 -0.41636 --0.097817 0.045961 -0.374305 --0.17422 -0.079079 -0.788737 -0.169811 -0.096837 -0.930936 -0.066086 0.088146 0.321504 --0.149399 -0.119766 -0.783049 --0.186242 -0.062921 0.337147 --0.16574 -0.102936 -0.790708 -0.000149 0.001448 0.814988 --0.191474 -0.010617 0.245282 -0.057009 -0.098046 -0.058783 -0.345692 0.040527 -0.133543 -0.261673 0.008906 0.072566 -0.207601 0.034662 -0.964126 -0.069559 -0.002266 0.673485 -0.361242 0.06737 -0.13841 --0.017244 -0.065856 0.221488 -0.127126 0.125889 0.440487 --0.218422 0.022526 0.260721 -0.209082 -0.067097 0.348097 -0.176827 -0.072432 0.374373 -0.186123 -0.06296 0.337175 -0.161442 0.11035 -0.974177 -0.137745 0.119463 -0.96321 -0.237267 0.007735 0.314909 --0.073333 0.00819 0.671301 -0.250165 -0.012465 0.317687 -0.255373 0.003608 0.276391 -0.079771 -0.101541 -0.132485 -0.133483 0.029525 -0.373842 --0.2092 -0.067057 0.348069 -0.1605 -0.112745 -0.963402 -0.213158 -0.059199 0.297507 -0.191524 -0.05003 0.289964 -0.054443 -0.034838 0.575107 -0.054752 -0.015075 0.563712 -0.119634 -0.051103 -0.913871 --0.17563 -0.080145 0.523413 -0.146787 -0.021377 0.536589 -0.097999 0.024274 0.525773 -0.094615 0.015938 0.534984 --0.309169 0.052202 0.035263 --0.308531 0.029294 0.103042 --0.298682 0.049946 0.106018 --0.297572 0.066955 -0.047369 --0.299564 0.033494 0.150195 --0.291675 0.007987 0.193759 --0.277382 0.037895 0.197003 -0.231763 -0.00842 0.365842 --0.301845 0.003632 0.142656 --0.277477 0.054656 0.148803 --0.247437 0.050371 0.189756 --0.054748 0.033272 0.526506 -0.122437 -0.113899 0.007519 --0.193282 0.020798 -0.942916 -0.073563 0.142338 0.425029 -0.066203 0.062522 0.091972 -0.000232 0.073015 0.137844 --0.047791 0.043658 0.604435 -0.212468 0.008589 0.36307 -0.176166 0.106477 -0.95205 -0.178626 -0.029829 0.287186 --0.079237 0.005393 0.695935 --0.076123 0.021169 -0.417027 --0.169275 -0.04711 0.331153 -0.216739 -0.026752 0.424907 -0.000277 -0.068158 0.778329 -0.191192 -0.081347 0.407258 --0.098086 0.024286 0.525765 -0.350164 0.122984 -0.143516 -0.183996 0.007199 0.388155 -0.212216 -0.004421 0.405766 --0.184116 0.007238 0.38813 -0.10786 0.133032 0.444293 --0.315013 0.1033 -0.056114 -0.343636 0.114749 -0.122414 -0.105901 0.15086 0.422464 -0.087645 0.135578 0.442222 --0.279045 0.060032 0.097165 -0.018609 0.031918 0.534297 -0.109621 0.089465 -0.973165 -0.100191 0.074932 -0.964246 --0.146831 -0.018057 -0.697226 -0.21641 -0.058012 0.417767 -0.065129 -0.080297 -0.613455 -0.067307 -0.090024 -0.544694 -0.055978 -0.05627 -0.544923 --0.228896 -0.055598 0.359671 -0.153721 -0.115869 -0.552604 -0.133373 -0.106461 -0.501061 -0.12403 -0.130059 -0.552657 -0.192908 0.007033 0.426625 -0.15375 -0.002896 0.38574 -0.169726 -0.000509 0.366447 --0.062061 0.009439 0.652188 --0.056839 0.020863 0.627445 --0.121258 -0.014342 -0.700516 --0.214751 -0.048761 0.251994 --0.212159 -0.041729 0.226274 --0.197533 -0.033816 0.24581 -0.173921 -0.082221 -0.963161 -0.168202 -0.061975 -0.975816 -0.177887 -0.043544 -0.957618 --0.095604 -0.024233 -0.703071 --0.102468 -0.052468 -0.79493 --0.198954 -0.008168 0.473288 -0.105332 -0.111549 -0.498398 -0.092592 -0.120714 -0.548836 --0.213277 -0.05916 0.297479 --0.191642 -0.049991 0.289936 -0.359854 0.065564 -0.149794 -0.14721 0.014138 0.493835 -0.175852 -0.005093 0.511355 -0.181789 -0.036859 -0.610575 -0.170095 -0.017463 -0.554248 -0.17871 -0.046872 -0.552482 -0.079132 -0.094498 -0.497075 --0.031564 0.064185 0.586557 --0.068814 -0.002386 0.673389 -0.144645 -0.133741 -0.615754 --0.169618 -0.057919 -0.788991 --0.164544 -0.02418 0.330059 --0.178745 -0.029791 0.287159 --0.353449 0.100297 -0.144988 -0.170909 -0.110143 -0.611958 -0.173346 -0.081978 -0.551082 -0.144451 0.001541 -0.553988 -0.184164 -0.070446 -0.608835 -0.058245 0.041798 0.617799 --0.359902 0.069812 -0.126473 --0.351593 0.071362 -0.14978 --0.348792 0.075551 -0.122467 -0.052327 0.025048 0.537409 -0.156605 -0.088769 -0.502422 -0.113103 -0.135944 -0.616971 -0.061286 -0.046683 -0.614011 -0.060984 -0.025141 -0.547473 -0.059732 -0.036682 -0.498685 -0.072668 -0.018394 -0.615161 --0.308402 0.029099 0.014168 --0.320004 0.067021 -0.007001 -0.06213 -0.067852 -0.496605 --0.112863 -0.115721 -0.78648 -0.122134 0.107746 -0.974322 -0.084451 -0.117277 -0.615599 --0.09246 -0.103354 -0.708364 --0.101836 -0.096328 -0.797447 -0.000687 0.081648 0.798072 -0.015952 0.101963 0.777649 --0.014695 0.101868 0.777555 -0.017383 0.060739 0.806361 -0.189972 0.015591 -0.975093 -0.191862 -0.004379 -0.960993 -0.000267 -0.024418 0.808928 --0.184152 -0.052972 0.530994 -0.180228 -0.027037 0.531658 -0.132653 -0.001324 -0.614735 --0.293724 0.071226 0.020867 -0.164928 0.009787 0.470594 -0.165244 -0.011569 -0.613368 -0.33453 0.113225 -0.100287 --0.317145 0.038512 -0.015189 --0.235384 -0.053793 0.306905 --0.165729 -0.069459 -0.874049 -0.168543 -0.057623 -0.504902 -0.169155 -0.04715 0.33118 -0.000295 -0.048076 0.797157 -0.100339 -0.002801 -0.615402 --0.24952 -0.035943 0.314613 --0.344146 0.066069 -0.129231 --0.353663 0.060882 -0.126628 -0.321049 0.068382 -0.007936 --0.233519 -0.049564 0.232094 --0.176089 -0.005058 0.511692 -0.065548 -0.047106 -0.461278 --0.237912 -0.032574 0.366035 -0.354767 0.102019 -0.146066 --0.049903 0.064645 0.606883 -0.092275 -0.088632 -0.463041 --0.215753 0.016943 0.30734 --0.19094 0.010127 0.346385 -0.347086 0.098279 -0.123603 --0.250285 -0.012426 0.317661 --0.23188 -0.008467 0.365694 -0.119313 0.028689 0.512206 --0.212585 0.008554 0.362939 --0.212317 -0.0049 0.405009 -0.156511 -0.038022 0.355508 --0.027271 0.106695 0.664112 --0.237387 0.007774 0.314882 -0.100543 -0.091974 0.537372 --0.061758 0.062859 0.633349 --0.066238 -0.090029 -0.544722 --0.0584 0.041725 0.617799 --0.066718 0.02961 0.643738 --0.081869 -0.044364 -0.703365 --0.096247 -0.072048 -0.795993 --0.171555 -0.034578 -0.692527 -0.153797 -0.057313 0.371336 --0.080103 -0.073706 -0.706604 --0.216857 -0.026786 0.424776 --0.14007 -0.023534 -0.900373 -0.125964 0.090476 0.363992 -0.096007 0.076435 0.350796 -0.138416 0.051197 0.39081 --0.052406 0.099858 0.672279 --0.058922 0.096015 0.666199 --0.082735 0.018206 0.705165 -0.088088 -0.111252 0.1119 -0.122322 -0.083917 0.107098 -0.094708 -0.129284 0.049443 -0.111728 -0.112324 0.082435 -0.06644 -0.127597 0.083167 -0.117103 0.023259 0.342647 -0.024221 -0.111692 0.078087 -0.049197 -0.110788 0.111325 -0.020854 -0.089789 0.106589 --0.039595 0.038126 -0.123114 -0.196143 -0.082017 0.459588 -0.071653 0.049761 0.052075 -0.068917 -0.092263 0.142257 -0.153351 -0.080547 0.40949 -0.162002 -0.095627 0.452908 -0.17152 -0.09458 0.495315 -0.138631 0.086743 0.448996 --0.262284 -0.016136 0.272303 -0.115985 -0.03189 -0.947976 --0.240876 0.018577 0.268688 --0.021682 -0.068079 0.181314 -0.024394 -0.068695 0.1802 -0.103653 -0.086124 -0.928478 -0.118736 -0.11038 -0.897024 -0.023641 -0.047052 0.792444 -0.130924 -0.08206 0.442419 -0.163385 -0.097974 -0.977937 -0.065571 0.074633 0.705359 -0.075525 0.044237 0.706769 -0.155403 0.080724 -0.978019 --0.200349 -0.031877 0.511405 --0.180522 -0.027003 0.532176 -0.316374 0.105045 -0.057311 -0.298933 0.0687 -0.048566 -0.101867 0.027765 0.144359 -0.078465 0.050928 0.154908 --0.255493 0.003647 0.276365 -0.044808 0.051491 0.797208 -0.056032 0.025116 0.792472 -0.057676 0.061247 0.779862 -0.032619 0.076882 0.792434 -0.044295 0.090641 0.76996 -0.153823 -0.023485 0.066935 -0.140312 -0.041279 0.102405 -0.143659 -0.073205 0.071343 -0.044913 -0.130195 0.048916 -0.328643 0.107482 -0.089929 -0.359103 0.120508 -0.142271 --0.116711 -0.063894 -0.872701 -0.037623 0.015333 0.806564 -0.329793 0.106175 -0.051768 -0.013631 -0.065663 0.215941 -0.018712 0.004167 0.813279 --0.202019 -0.01911 0.207767 -0.03988 -0.080503 0.145011 -0.11583 0.00439 -0.951472 -0.063954 0.068662 0.757824 --0.088107 -0.10108 0.514606 -0.304269 0.044884 -0.059759 -0.071426 0.032298 0.769955 -0.33071 0.036433 -0.056124 --0.059257 0.073308 0.277159 -0.067868 -0.030914 -0.431164 -0.051605 0.094388 0.699984 -0.308124 0.031521 -0.022304 --0.223946 -0.021616 0.161188 --0.209652 0.008291 0.164432 -0.11894 0.10209 0.463365 -0.179601 -0.092619 -0.689941 --0.302907 0.043139 -0.058563 --0.225476 -0.039879 0.205727 -0.067115 0.072045 0.732783 -0.049534 0.098277 0.740572 --0.037865 0.106055 0.663632 -0.134121 -0.101168 0.050634 -0.049119 -0.08204 0.316253 -0.075891 -0.017079 0.704095 -0.145693 0.082887 0.387494 -0.148513 0.077463 0.419952 --0.020141 0.076839 0.079383 --0.073478 0.071222 -0.125881 -0.18373 -0.061424 -0.687579 --0.119577 0.075674 -0.127002 --0.256679 0.051936 0.08732 --0.252373 0.033449 0.076668 --0.08359 -0.11721 -0.615163 --0.155435 0.052239 -0.127829 -0.140036 -0.126156 -0.962029 -0.112933 0.060372 -0.975334 --0.060424 -0.046616 -0.613575 --0.071807 -0.018327 -0.614725 -0.126995 -0.004658 0.124934 -0.121818 0.020068 -0.968705 --0.132384 0.052987 -0.930089 -0.020565 -0.022346 0.80643 -0.172617 -0.034573 -0.6925 -0.075579 -0.022709 -0.438903 -0.067669 -0.009307 -0.416099 -0.14779 -0.018088 -0.697429 -0.127355 -0.021293 -0.963114 -0.081894 -0.04472 -0.705653 -0.095626 -0.024589 -0.705358 -0.121488 -0.014626 -0.70234 --0.168278 -0.059321 -0.917305 -0.079233 0.03866 0.740287 --0.261791 0.008945 0.072539 --0.231986 0.004352 0.117807 -0.21154 -0.046599 0.468037 -0.198718 -0.008203 0.472951 -0.043258 0.071539 0.141414 --0.127551 -0.012406 -0.929538 --0.026188 0.058074 0.012186 -0.112256 0.042497 -0.375606 -0.134501 0.011415 -0.414821 -0.026596 -0.068706 0.768445 -0.097698 0.045923 -0.374278 -0.137736 -0.005975 -0.414093 -0.122283 -0.115801 -0.974611 --0.064269 -0.08023 -0.613019 -0.19953 -0.031896 0.50925 -0.10673 -0.105034 -0.952919 --0.174945 -0.096887 -0.930936 --0.166068 -0.090536 -0.885475 -0.137206 0.059278 0.473049 -0.108901 0.069512 0.488713 -0.043453 -0.067073 0.234408 -0.148483 0.0426 0.439737 --0.121795 0.024085 -0.939902 -0.070013 -0.024905 -0.463256 -0.094521 0.054911 -0.332617 --0.044988 -0.069707 0.571411 -0.196549 -0.06939 0.502664 --0.269302 0.012103 0.241456 -0.139593 -0.085551 -0.97833 --0.1813 0.106426 -0.95205 -0.084087 -0.118334 -0.0167 -0.070692 -0.131623 0.017117 --0.027246 0.032932 -0.019514 -0.068233 0.043141 -0.330533 -0.169942 -0.022509 -0.294929 --0.066197 0.062519 0.092052 --0.246955 0.034265 0.236012 -0.000944 0.079609 0.57354 -0.157193 0.037473 -0.975575 --0.017007 0.074555 0.577188 --0.092451 0.046276 0.119469 --0.101966 0.027771 0.14434 -0.148856 -0.027476 -0.970453 -0.357818 0.09714 -0.107473 --0.253954 -0.017301 0.119624 --0.062212 -0.069614 0.179375 --0.118028 0.064277 0.011804 --0.165178 0.032598 -0.050201 --0.078283 0.059413 0.009658 --0.139111 0.065256 -0.050545 --0.067102 -0.127608 0.083129 --0.093764 -0.129297 0.049364 --0.044257 -0.130188 0.048792 --0.02388 -0.118223 0.017593 --0.133511 -0.101327 0.05037 --0.070802 -0.131593 0.016976 --0.045983 -0.121947 -0.007695 --0.12708 -0.004649 0.124927 --0.122402 -0.083924 0.107013 -0.042901 -0.013411 0.798205 --0.110434 -0.112461 0.082173 --0.300537 0.030467 -0.022794 -0.064719 -0.006094 0.777678 -0.06977 -0.03297 0.746087 -0.079517 0.002111 0.744329 --0.097479 0.07402 -0.050754 -0.04895 -0.041262 0.777678 -0.051476 -0.062134 0.744591 -0.077936 0.061074 -0.282267 -0.049416 0.035999 -0.279656 -0.085 0.027706 -0.418945 -0.093614 0.023107 -0.434949 -0.083504 0.018742 -0.434733 -0.10551 0.024301 -0.434957 -0.099179 0.017905 -0.450637 -0.104225 0.010943 -0.473395 -0.093515 0.007878 -0.466875 -0.085533 -0.001387 -0.479578 -0.086084 0.003223 -0.460441 -0.079785 0.00487 -0.444702 -0.075304 0.009084 -0.435316 -0.069238 0.013881 -0.415505 -0.064922 0.011603 -0.400495 -0.067382 0.022857 -0.391336 -0.071056 0.033647 -0.36988 -0.075886 0.030472 -0.393918 -0.086388 0.036786 -0.396518 -0.087717 0.044492 -0.374683 -0.096522 0.042056 -0.387442 -0.107434 0.038476 -0.397641 -0.11772 0.03446 -0.397719 -0.111318 0.030022 -0.419755 -0.115082 0.022058 -0.434555 --0.154396 -0.04401 -0.821796 --0.146033 -0.03405 -0.792788 --0.14559 -0.038588 -0.836323 --0.151405 -0.029697 -0.744407 --0.138817 -0.024076 -0.746508 --0.133452 -0.013771 -0.699098 --0.128459 -0.022903 -0.747439 --0.117281 -0.025556 -0.748756 --0.126527 -0.031998 -0.793891 --0.123915 -0.04171 -0.82303 --0.136441 -0.037708 -0.836495 --0.144431 -0.036866 -0.866704 --0.140784 -0.036521 -0.889768 --0.14861 -0.032492 -0.887839 --0.148537 -0.019592 -0.89748 --0.156102 -0.036015 -0.894347 --0.163448 -0.037764 -0.906158 --0.161411 -0.050346 -0.895794 --0.16356 -0.066484 -0.898927 --0.163188 -0.058661 -0.874199 --0.167186 -0.064576 -0.838923 --0.162506 -0.054127 -0.837091 --0.16378 -0.04718 -0.789485 --0.17189 -0.048281 -0.739308 --0.162645 -0.037607 -0.742288 --0.161111 -0.024278 -0.695175 -0.034314 0.049359 0.593313 -0.031715 0.064242 0.586614 -0.039882 0.05637 0.594047 -0.018033 0.074655 0.577287 -0.027023 0.077375 0.582822 -0.035109 0.075342 0.589601 -0.043328 0.076991 0.600573 -0.042798 0.064854 0.596433 -0.049759 0.052827 0.603861 -0.055703 0.052902 0.614078 -0.06172 0.031236 0.625212 -0.054571 0.031488 0.61617 -0.04518 0.02485 0.613831 -0.042287 0.036067 0.605219 -0.025777 0.044468 0.593641 -0.014607 0.039923 0.594763 -0.008979 0.05128 0.586359 --0.007541 0.051232 0.586311 -0.00073 0.061307 0.579418 --0.004866 0.069214 0.575659 -0.006351 0.069261 0.575706 -0.009533 0.077746 0.574115 -0.137823 -0.072293 -0.448672 -0.14702 -0.076721 -0.464719 -0.144444 -0.066039 -0.45078 -0.147212 -0.087648 -0.480882 -0.156045 -0.07636 -0.482139 -0.164822 -0.073968 -0.503427 -0.161374 -0.062994 -0.483552 -0.163648 -0.047787 -0.484884 -0.157219 -0.052742 -0.467207 -0.150737 -0.045026 -0.452725 -0.149314 -0.055426 -0.451855 -0.142063 -0.058432 -0.430772 -0.141529 -0.051298 -0.399741 -0.13082 -0.059998 -0.402149 -0.121509 -0.065181 -0.367875 -0.121133 -0.064061 -0.400636 -0.10807 -0.065477 -0.398856 -0.125755 -0.068748 -0.427151 -0.129082 -0.076721 -0.446488 -0.117464 -0.078584 -0.445528 -0.127782 -0.087858 -0.46154 -0.123989 -0.099707 -0.478668 -0.136715 -0.094357 -0.479887 -0.146434 -0.100096 -0.501818 -0.0751 -0.005894 -0.439339 -0.068813 0.003522 -0.420396 -0.060073 0.00326 -0.38662 -0.058085 -0.009248 -0.386583 -0.052855 0.004846 -0.358571 -0.045201 0.005832 -0.332608 -0.053793 0.018972 -0.343158 -0.056407 0.030966 -0.330399 -0.064471 0.033784 -0.344822 -0.07639 0.0447 -0.34469 -0.072056 0.038791 -0.352654 -0.079377 0.042965 -0.360043 --0.322854 0.106248 -0.077181 --0.313158 0.092123 -0.066962 --0.320502 0.095611 -0.078823 --0.333369 0.102499 -0.098747 --0.346526 0.104262 -0.113642 --0.337765 0.107296 -0.110064 --0.338259 0.116265 -0.110842 -0.017746 0.040951 0.812066 -0.009216 0.030475 0.814915 -0.020663 0.028746 0.813476 --0.00773 0.030427 0.814869 -0.000519 0.020385 0.816414 --0.005597 0.011172 0.816161 -0.006185 0.011219 0.816208 -0.009217 -0.000749 0.81448 -0.015179 0.018604 0.815181 -0.027084 0.022222 0.812192 -0.028174 0.010026 0.810997 -0.035581 0.02961 0.807508 -0.044336 0.035043 0.802024 -0.037243 0.045818 0.804088 -0.03363 0.059166 0.801916 -0.025828 0.050636 0.807364 -0.009273 0.050657 0.810793 -0.000687 0.061143 0.808539 --0.007787 0.05061 0.810747 --0.024567 0.050517 0.807247 --0.016261 0.040856 0.811973 --0.019401 0.028627 0.813359 -0.18904 0.05325 -0.939661 -0.182748 0.035737 -0.933876 -0.195966 0.039643 -0.94641 -0.164345 0.034493 -0.924958 -0.174357 0.016942 -0.928091 -0.167372 -0.001747 -0.921102 -0.179159 0.00241 -0.937492 -0.183653 -0.011515 -0.948821 -0.192301 0.007064 -0.952195 -0.200541 0.015069 -0.963765 -0.200717 0.0255 -0.955328 -0.206609 0.045938 -0.955087 -0.212793 0.056229 -0.96256 -0.209337 0.06592 -0.955087 -0.201419 0.088055 -0.954943 -0.201847 0.076324 -0.949784 -0.193309 0.085722 -0.948339 -0.192192 0.06995 -0.943036 -0.174661 0.068452 -0.937492 -0.162798 0.083759 -0.940143 -0.150956 0.069695 -0.933635 -0.12904 0.072825 -0.934358 -0.146932 0.05325 -0.926886 -0.14579 0.034745 -0.918932 --0.022539 -0.100064 0.091452 --0.034476 -0.101114 0.108583 --0.037401 -0.114645 0.094311 --0.030398 -0.087436 0.126216 --0.046951 -0.094812 0.127269 --0.056899 -0.086745 0.145514 --0.0631 -0.10197 0.128565 --0.079022 -0.103607 0.1285 --0.070143 -0.11564 0.112648 --0.079179 -0.122321 0.098194 --0.056918 -0.122012 0.097079 --0.043539 -0.124662 0.081103 --0.055252 -0.131201 0.065892 --0.03184 -0.122629 0.064184 --0.021241 -0.119156 0.047819 --0.014697 -0.106949 0.062432 --0.005273 -0.087201 0.062205 --0.011532 -0.094401 0.076493 --0.011787 -0.086838 0.089827 --0.004127 -0.078303 0.089556 --0.007918 -0.081657 0.10567 -0.000538 -0.082118 0.117488 --0.014592 -0.081206 0.124929 --0.022503 -0.076678 0.144759 -0.131016 0.030007 -0.921102 -0.143786 0.015279 -0.910738 -0.14456 -0.003872 -0.904471 -0.155358 -0.003662 -0.909773 -0.1614 -0.021081 -0.914594 -0.119759 0.090982 -0.939902 -0.114652 0.074994 -0.938938 -0.10756 0.073056 -0.946893 -0.113518 0.057939 -0.939902 -0.111983 0.043341 -0.944241 -0.119362 0.03847 -0.932912 -0.123592 0.019284 -0.927128 -0.122701 0.016798 -0.434688 -0.119816 0.015003 -0.450125 -0.123458 0.007119 -0.467249 -0.115291 0.010284 -0.473285 -0.110572 0.006901 -0.501334 -0.123614 0.037812 -0.375614 -0.126764 0.02868 -0.397726 -0.136835 0.018556 -0.394055 -0.127983 0.018983 -0.419896 -0.128589 0.010047 -0.432236 -0.13734 0.003116 -0.416256 -0.130069 -6.1e-05 -0.434212 -0.133967 -0.011808 -0.420916 -0.128438 -0.005142 -0.442843 -0.130463 -0.011981 -0.449293 -0.127148 -0.001314 -0.460421 -0.131875 0.000305 -0.482078 -0.07376 -0.060361 -0.446447 -0.081422 -0.070317 -0.446155 -0.074366 -0.059211 -0.42425 -0.083033 -0.080999 -0.460094 -0.092136 -0.079874 -0.450203 -0.101872 -0.09105 -0.460544 -0.104184 -0.079577 -0.445754 -0.101264 -0.071654 -0.425042 -0.095197 -0.066519 -0.397707 -0.081907 -0.063519 -0.3956 -0.065807 -0.059207 -0.358372 -0.070643 -0.057006 -0.394146 -0.063182 -0.044838 -0.390651 -0.049602 -0.032254 -0.350668 -0.058213 -0.033789 -0.389715 -0.060478 -0.025451 -0.402212 -0.064594 -0.038002 -0.423824 -0.068956 -0.050201 -0.446956 -0.067741 -0.059835 -0.460462 -0.063104 -0.056832 -0.477018 -0.067905 -0.071221 -0.476469 -0.068524 -0.082087 -0.496373 -0.075535 -0.082429 -0.476394 -0.086245 -0.092693 -0.476471 --0.13985 -0.032976 0.544864 --0.121856 -0.015987 0.544631 --0.157599 -0.047413 0.542014 --0.138462 -0.044574 0.546762 --0.118631 -0.049816 0.550605 --0.118026 -0.037195 0.550138 --0.093064 -0.030237 0.555935 --0.109946 -0.022646 0.549189 --0.095244 -0.014451 0.549912 --0.07089 -0.019631 0.558345 --0.070176 -0.006256 0.552081 --0.052827 -0.000471 0.559068 --0.061982 0.00917 0.546538 --0.052401 0.02565 0.53756 --0.071502 0.020137 0.538825 --0.097721 0.006941 0.540286 --0.115382 0.000856 0.538372 --0.119433 0.007059 0.530845 --0.111344 0.022729 0.520377 --0.132721 0.00573 0.523074 --0.1349 0.020916 0.511005 --0.151237 -0.002315 0.520796 --0.162466 0.000964 0.509945 --0.165064 -0.01331 0.526696 --0.178461 -0.01867 0.527382 --0.171972 -0.028404 0.534845 --0.183187 -0.037055 0.533602 --0.17008 -0.044257 0.538687 --0.169844 -0.059164 0.536704 --0.011435 -0.071785 0.162323 --0.033151 -0.071885 0.162021 --0.043355 -0.069196 0.184369 --0.050995 -0.074575 0.161725 --0.067752 -0.080186 0.156535 --0.079645 -0.075422 0.161214 --0.081894 -0.09013 0.145433 --0.096184 -0.097408 0.127305 -0.155193 -0.042267 -0.328511 -0.144044 -0.056138 -0.331802 -0.146565 -0.046701 -0.362179 -0.14187 -0.067816 -0.300288 -0.129697 -0.065918 -0.33509 -0.112584 -0.069761 -0.334921 -0.145724 -0.04062 -0.397722 -0.14768 -0.037354 -0.429624 -0.14385 -0.031198 -0.410293 -0.13465 -0.019295 -0.431087 -0.139795 -0.022598 -0.40794 -0.144662 -0.015466 -0.390082 -0.147078 -0.005504 -0.387421 -0.156843 -0.015484 -0.353528 -0.16598 -0.010938 -0.323817 -0.162478 -0.027604 -0.325149 -0.165205 -0.038736 -0.295989 -0.17237 -0.033081 -0.260025 -0.16446 -0.050074 -0.260846 -0.161644 -0.06222 -0.2196 -0.152275 -0.065284 -0.261854 -0.137313 -0.078934 -0.26276 --0.035914 0.029887 0.535065 --0.035627 0.023916 0.550875 --0.045107 -0.011677 0.572079 --0.044876 0.004108 0.5716 --0.044648 0.008032 0.590438 --0.040072 0.016353 0.569512 --0.037008 0.012413 0.596277 --0.030016 0.020549 0.569024 --0.022786 0.024164 0.555526 --0.027801 0.026911 0.545729 --0.027459 0.037248 0.523334 --0.15181 -0.045272 0.070061 --0.143513 -0.057344 0.089619 --0.147568 -0.033066 0.086242 --0.133135 -0.081212 0.091882 --0.134142 -0.064362 0.105115 --0.121507 -0.066958 0.122248 --0.130141 -0.046087 0.118848 --0.125061 -0.026671 0.131923 --0.135215 -0.024117 0.114596 --0.135435 -0.002784 0.106996 --0.143273 -0.018113 0.097486 --0.150184 -0.01141 0.074829 --0.152458 0.008475 0.053013 --0.157561 -0.009098 0.049444 --0.166125 -0.015147 0.017548 --0.15867 -0.030222 0.04676 --0.154641 -0.056404 0.046788 --0.159489 -0.064526 0.015852 --0.149614 -0.077279 0.04327 --0.14235 -0.093183 0.038038 --0.138852 -0.091657 0.060299 --0.123816 -0.106646 0.066798 --0.129778 -0.095184 0.080177 --0.118404 -0.100227 0.094179 --0.170441 -0.04876 -0.016736 --0.162724 -0.069586 -0.017523 --0.163057 -0.075771 -0.054285 --0.151345 -0.088426 -0.018273 --0.136028 -0.10162 -0.018561 --0.140846 -0.09927 0.00987 --0.129984 -0.108136 0.032549 --0.170585 -0.002107 -0.013763 --0.173047 -0.025564 -0.015013 --0.178563 -0.035749 -0.052958 --0.181919 -0.023271 -0.091209 --0.179103 -0.044763 -0.092663 --0.17597 -0.052477 -0.134767 --0.171315 -0.06396 -0.093206 --0.1577 -0.080727 -0.092798 -0.131039 0.13736 0.411877 -0.140198 0.119042 0.411468 -0.135377 0.131816 0.400307 -0.144295 0.099758 0.424146 -0.146946 0.09759 0.408961 -0.148633 0.077584 0.402356 -0.146465 0.09759 0.394257 -0.141644 0.100482 0.37859 -0.139715 0.118559 0.38881 -0.130556 0.136395 0.392112 -0.125229 0.136629 0.384559 -0.123446 0.146097 0.401633 -0.113217 0.154187 0.409708 -0.122121 0.147965 0.414047 -0.127664 0.139047 0.421037 -0.123473 0.137444 0.430974 -0.132243 0.124585 0.432438 -0.132484 0.110605 0.445597 -0.139715 0.103616 0.435956 -0.1455 0.079994 0.435619 -0.105464 -0.079538 -0.965452 -0.108877 -0.059028 -0.964005 -0.113441 -0.073235 -0.973405 -0.112164 -0.048839 -0.948579 -0.114524 -0.041362 -0.959667 -0.118654 -0.025019 -0.956534 -0.124001 -0.034779 -0.963282 -0.13177 -0.046317 -0.96979 -0.14364 -0.059065 -0.97437 -0.126855 -0.067107 -0.975575 -0.120626 -0.088659 -0.978708 -0.12822 -0.105502 -0.979672 -0.115311 -0.104016 -0.975093 -0.110603 -0.112693 -0.967861 -0.106386 -0.09863 -0.966175 -0.102106 -0.0885 -0.953641 -0.104124 -0.095288 -0.938938 -0.103842 -0.077671 -0.938697 -0.107859 -0.071404 -0.923031 -0.110488 -0.058521 -0.935082 -0.11693 -0.040685 -0.934599 --0.074463 -0.077453 0.307102 --0.078401 -0.059789 0.264934 --0.060105 -0.068898 0.271716 --0.094806 -0.048793 0.261802 --0.092646 -0.040849 0.223745 --0.098855 -0.019207 0.193097 --0.093596 -0.035382 0.193387 --0.097932 -0.038457 0.172604 --0.082951 -0.049343 0.190749 --0.078645 -0.064003 0.179784 --0.067216 -0.061508 0.206435 --0.051503 -0.065799 0.210524 --0.056095 -0.066328 0.240663 --0.042784 -0.069414 0.257042 --0.030736 -0.06768 0.230518 --0.025676 -0.071542 0.257965 --0.009122 -0.069386 0.246128 --0.02034 -0.08079 0.311141 --0.015722 -0.091002 0.38749 --0.038388 -0.094059 0.388783 --0.063779 -0.102674 0.454682 --0.069124 -0.099458 0.402922 --0.099713 -0.101902 0.417736 --0.082542 -0.092616 0.359743 --0.092785 -0.07983 0.333374 --0.110242 -0.06956 0.339931 --0.102354 -0.060438 0.304457 --0.103764 -0.036518 0.268755 --0.167933 0.08371 -0.940143 --0.179796 0.068401 -0.937492 --0.156091 0.069644 -0.933635 --0.197328 0.069899 -0.943036 --0.194175 0.053199 -0.939661 --0.201101 0.039592 -0.94641 --0.188074 0.035687 -0.933876 --0.179874 0.016891 -0.928091 --0.169862 0.034443 -0.924958 --0.151499 0.034695 -0.918932 --0.152259 0.053199 -0.926886 --0.134175 0.072774 -0.934358 --0.119787 0.074944 -0.938938 --0.124894 0.090931 -0.939902 --0.123458 0.104295 -0.947616 --0.140525 0.105246 -0.944 --0.164355 0.112976 -0.95099 --0.161647 0.101154 -0.944964 --0.1825 0.094357 -0.947134 --0.198444 0.085672 -0.948339 --0.206982 0.076274 -0.949784 --0.26955 -0.027566 0.148414 --0.269784 -0.031868 0.174756 --0.283577 -0.021435 0.15516 --0.254123 -0.038395 0.194776 --0.268151 -0.032264 0.201521 --0.266811 -0.032233 0.222687 --0.27862 -0.020605 0.208538 --0.283485 -0.005696 0.214455 --0.290721 -0.008551 0.188788 --0.298912 0.005133 0.168092 --0.294046 -0.009776 0.162176 --0.295288 -0.009656 0.136587 --0.302695 0.003478 0.118243 --0.294271 -0.005903 0.112597 --0.294476 0.000933 0.081539 --0.281762 -0.011371 0.106581 --0.268831 -0.011324 0.101957 --0.268751 -0.021254 0.123824 --0.253482 -0.027507 0.142668 --0.238511 -0.021268 0.139045 --0.237648 -0.031749 0.163265 --0.223083 -0.032097 0.185407 --0.238055 -0.038335 0.18903 --0.240273 -0.043833 0.209926 --0.092235 -0.053103 0.557851 --0.065838 -0.057596 0.565579 --0.068238 -0.048313 0.5663 --0.06415 -0.06796 0.563892 --0.052196 -0.056957 0.57459 --0.039091 -0.057596 0.588712 --0.044625 -0.051689 0.58389 --0.046306 -0.037344 0.587499 --0.054252 -0.04554 0.57714 --0.069674 -0.038068 0.56485 --0.070156 -0.028909 0.562199 --0.110436 -0.062109 0.551569 --0.113088 -0.075607 0.547471 --0.091271 -0.07118 0.55544 --0.061976 -0.078684 0.556998 --0.307034 0.016774 0.123519 --0.308559 0.021113 0.09218 --0.309478 0.03837 0.071997 --0.310004 0.030432 0.052442 --0.31328 0.040791 0.018598 --0.304347 0.019634 0.047448 --0.29708 0.01208 0.043322 --0.301366 0.02206 0.010747 --0.286625 0.011456 0.039936 --0.278617 0.015978 0.038748 --0.271092 0.000358 0.073531 --0.255479 -0.00576 0.098726 --0.10819 0.051484 0.346852 --0.091984 0.065889 0.328944 --0.101225 0.039468 0.317848 --0.099468 0.030274 0.292639 --0.11085 0.013589 0.310555 --0.119912 -0.008526 0.319179 --0.125871 0.000417 0.343807 --0.129094 -0.019956 0.347048 --0.14803 0.06051 0.427228 --0.148512 0.060269 0.40915 --0.148753 0.077624 0.402329 --0.144415 0.068705 0.390109 --0.139835 0.083407 0.375333 --0.131157 0.07232 0.373959 --0.113804 0.08148 0.361907 --0.165551 0.020785 -0.013373 --0.173778 0.01109 -0.05048 --0.172771 0.023123 -0.088693 --0.179595 0.000111 -0.089983 --0.182354 -0.010619 -0.132109 --0.255119 -0.04115 0.231815 --0.262239 -0.034719 0.239786 --0.256245 -0.039171 0.250488 --0.263742 -0.029584 0.251808 --0.261051 -0.028069 0.269964 --0.267756 -0.016443 0.253074 --0.267568 -0.003742 0.255398 --0.275969 -0.003485 0.237115 --0.282221 0.010851 0.218632 --0.229679 -0.046571 0.221176 --0.24261 -0.046618 0.225799 --0.245496 -0.048147 0.236884 --0.347738 0.040042 -0.142918 --0.353667 0.042125 -0.127058 --0.356317 0.046678 -0.138321 --0.35461 0.040786 -0.15294 -0.005749 0.069597 0.154621 --0.016153 0.071343 0.154745 --0.022133 0.07466 0.135914 --0.030434 0.070256 0.160134 --0.048125 0.065048 0.164629 --0.033519 0.068932 0.193502 --0.035154 0.071296 0.236562 --0.016637 0.072116 0.217562 -0.001976 0.076208 0.235974 --0.001783 0.071803 0.195631 -0.007337 0.068129 0.165708 -0.029494 0.021295 0.609461 -0.015004 0.021967 0.592485 -0.015432 0.027967 0.60197 -0.000534 0.033823 0.598879 -0.022632 0.119945 0.709601 -0.01864 0.122279 0.72591 -0.030456 0.116728 0.725309 -0.007049 0.124805 0.725863 -0.014014 0.119953 0.745819 -0.007809 0.113608 0.764353 -0.022299 0.110654 0.76278 -0.030941 0.098067 0.775004 -0.035492 0.104674 0.76056 -0.047384 0.096111 0.756079 -0.039022 0.10842 0.743 -0.041374 0.10915 0.725558 -0.051577 0.098428 0.724437 -0.043447 0.106507 0.711471 -0.043852 0.103614 0.701769 -0.035145 0.109797 0.700616 -0.026544 0.112935 0.696855 -0.016846 0.119048 0.696408 -0.003831 0.121368 0.693726 -0.001843 0.12455 0.707691 --0.005359 0.124757 0.725816 --0.047895 -0.033112 0.632439 --0.039997 -0.050565 0.611532 --0.039458 -0.048356 0.630012 --0.033538 -0.067356 0.584232 --0.041964 -0.080732 0.563843 --0.027989 -0.076875 0.581195 --0.014495 -0.083384 0.576854 --0.031229 -0.063274 0.607769 --0.035367 -0.055128 0.641799 --0.027961 -0.062423 0.652942 --0.044647 -0.052561 0.666889 --0.050951 -0.056102 0.687794 --0.06074 -0.040712 0.687247 --0.070191 -0.034184 0.705635 --0.068844 -0.025178 0.6872 --0.071198 -0.011523 0.684393 --0.066181 -0.011895 0.672771 --0.064748 0.000376 0.663475 --0.05987 -0.005735 0.657821 --0.057325 0.002532 0.645455 --0.054149 -0.014155 0.644219 --0.049165 -0.019113 0.623133 --0.047295 -0.006621 0.608268 --0.046321 -0.021741 0.599809 --0.045823 -0.025293 0.583643 -0.034705 -0.078761 0.707976 -0.038589 -0.07787 0.725791 -0.025665 -0.084619 0.720957 -0.049905 -0.068353 0.725275 -0.040027 -0.073785 0.742972 -0.04014 -0.066351 0.760543 -0.027533 -0.077494 0.753043 -0.013803 -0.079631 0.757756 -0.013991 -0.085154 0.741814 -0.000149 -0.088699 0.731704 -0.013562 -0.088193 0.726908 -0.012945 -0.087998 0.713803 -0.000278 -0.088283 0.705279 -0.014016 -0.084427 0.698771 -0.017632 -0.076473 0.686237 -0.02777 -0.078283 0.694074 -0.04279 -0.069767 0.694988 -0.052085 -0.05596 0.687934 -0.053071 -0.063014 0.706322 -0.060455 -0.056201 0.725898 --0.124358 0.015972 0.116484 --0.130583 0.019448 0.097922 --0.122774 0.038074 0.084373 --0.134711 0.030922 0.074441 --0.136144 0.044633 0.047276 --0.145615 0.026361 0.05254 --0.155987 0.025318 0.021507 --0.344947 0.098323 -0.101601 --0.354748 0.089292 -0.091377 --0.351936 0.084312 -0.081462 --0.345374 0.094633 -0.072209 --0.352176 0.096955 -0.090405 --0.353859 0.107433 -0.10818 --0.330378 0.041738 -0.108605 --0.330965 0.052203 -0.106372 --0.321655 0.064517 -0.089461 --0.31604 0.055047 -0.082705 --0.306991 0.054927 -0.068061 --0.311581 0.043207 -0.073344 --0.309078 0.0326 -0.057447 --0.320561 0.033007 -0.078988 --0.339723 0.035357 -0.097872 --0.338386 0.036283 -0.110522 --0.343031 0.037898 -0.120054 --0.340279 0.050512 -0.128731 --0.335034 0.046233 -0.119727 -0.177365 0.094407 -0.947134 -0.156512 0.101205 -0.944964 -0.159221 0.113026 -0.95099 -0.13539 0.105296 -0.944 -0.118323 0.104346 -0.947616 -0.119172 0.111799 -0.956052 -0.107758 0.098325 -0.956293 -0.100776 0.089941 -0.964247 -0.102101 0.083022 -0.956534 -0.102727 0.067415 -0.955328 -0.204447 0.088714 -0.970754 -0.211592 0.068093 -0.96979 -0.199394 0.079435 -0.975334 -0.210757 0.047042 -0.970995 -0.204398 0.026319 -0.97196 -0.198123 0.038895 -0.975816 -0.178143 0.02451 -0.975575 -0.183176 0.049987 -0.977263 -0.158815 0.059781 -0.977745 -0.183612 0.069476 -0.977986 -0.181941 0.088311 -0.978226 -0.159791 0.099559 -0.977263 -0.178263 0.103536 -0.976057 -0.172326 0.114202 -0.9722 -0.19131 0.103879 -0.971959 -0.20086 0.096689 -0.964969 -0.005181 -0.087214 0.062214 -0.000179 -0.071911 0.07624 --5.2e-05 -0.073248 0.062226 -0.000429 -0.076374 0.04766 --0.004377 -0.088818 0.033195 --4.6e-05 -0.066889 0.02081 --4e-05 -0.073279 0.033488 -0.004541 -0.088818 0.033436 -0.010327 -0.099053 0.017474 -0.014908 -0.11014 0.033623 -0.033467 -0.126531 0.033382 -0.021891 -0.119156 0.04806 -0.032008 -0.122629 0.064184 -0.014856 -0.106955 0.062437 -0.011436 -0.094421 0.076506 -0.021981 -0.100084 0.091465 -0.012149 -0.086864 0.089845 -0.00887 -0.081677 0.105684 -0.004955 -0.078317 0.089565 -0.105559 0.049912 -0.953401 -0.110176 0.031911 -0.950026 -0.101499 0.059253 -0.963042 -0.100973 0.06804 -0.970754 -0.104367 0.051356 -0.969549 -0.117059 0.042578 -0.973164 -0.110071 0.033648 -0.965693 -0.116072 0.013433 -0.960631 -0.111821 0.023545 -0.95581 -0.114597 0.013327 -0.946169 -0.117434 -0.004796 -0.942795 -0.120635 0.005571 -0.934599 -0.126374 0.000526 -0.92062 -0.115697 0.052927 0.064604 -0.12412 0.057123 0.041569 -0.107076 0.06003 0.039268 -0.135992 0.0446 0.047299 -0.135003 0.05898 0.0145 -0.145382 0.056535 -0.015276 -0.128658 0.066975 -0.016655 -0.119967 0.074178 -0.051056 -0.108637 0.070634 -0.018096 -0.085949 0.067766 -0.01774 -0.097938 0.065546 0.010758 -0.089167 0.05798 0.035697 -0.071956 0.050993 0.033388 -0.082586 0.052298 0.053811 -0.080983 0.052559 0.072149 -0.094446 0.048808 0.075378 -0.10232 0.045278 0.09406 -0.109649 0.047386 0.082601 -0.122678 0.038054 0.084387 -0.130502 0.019442 0.097927 -0.134594 0.030903 0.074455 -0.14572 0.026341 0.052554 -0.156332 0.025299 0.02152 -0.152839 0.008468 0.053018 -0.124276 0.015966 0.116488 -0.135361 -0.002784 0.106996 -0.135135 -0.024123 0.114601 -0.143199 -0.018113 0.097486 -0.147489 -0.03303 0.086306 -0.150351 -0.01141 0.074829 -0.158212 -0.009098 0.049444 -0.159315 -0.030186 0.046824 -0.167008 -0.015154 0.017552 -0.173674 -0.025584 -0.015001 -0.171212 -0.002127 -0.013749 -0.173907 0.011057 -0.050457 -0.165909 0.020759 -0.013355 -0.156984 0.041113 -0.013512 -0.154395 0.051967 -0.049846 --0.124767 -0.083039 0.539078 --0.094399 -0.086967 0.543967 --0.074263 -0.095434 0.538294 --0.063175 -0.088685 0.5489 --0.181475 -0.068678 0.527955 --0.167436 -0.07399 0.532045 --0.164075 -0.086491 0.522695 --0.147763 -0.082629 0.53208 --0.124752 -0.090866 0.530217 --0.114596 -0.099145 0.514863 --0.094865 -0.097686 0.529563 --0.062976 -0.100331 0.523284 --0.148871 -0.086093 -0.13452 --0.140218 -0.093475 -0.092171 --0.118388 -0.101384 -0.091998 --0.130259 -0.101712 -0.053949 --0.119931 -0.109546 -0.023769 -0.08764 -0.093707 -0.216992 -0.081115 -0.082263 -0.256671 -0.100588 -0.085991 -0.260314 -0.063332 -0.074299 -0.253294 -0.072724 -0.072541 -0.293332 -0.067366 -0.063606 -0.326429 -0.079865 -0.068112 -0.330731 -0.091596 -0.066713 -0.364781 -0.095734 -0.069876 -0.333607 -0.108066 -0.078032 -0.299852 -0.120622 -0.085307 -0.262494 -0.130847 -0.089998 -0.220525 -0.139655 -0.088782 -0.177295 -0.118948 -0.097977 -0.176964 -0.105677 -0.103228 -0.133752 -0.095775 -0.101002 -0.17561 -0.073108 -0.096493 -0.173495 -0.058404 -0.094484 -0.130672 -0.055271 -0.085402 -0.17082 -0.04176 -0.069767 -0.168502 -0.052019 -0.074652 -0.210941 -0.049896 -0.062168 -0.250626 -0.000105 -0.093426 0.416664 -0.016088 -0.091022 0.387503 -0.014568 -0.097048 0.458884 -0.020875 -0.080817 0.311158 -0.038653 -0.094125 0.389 -0.06913 -0.099564 0.403338 -0.063146 -0.102773 0.454611 -0.092277 -0.103655 0.496372 -0.061235 -0.104008 0.501116 -0.062183 -0.100099 0.522373 -0.035222 -0.101386 0.506885 -0.010127 -0.099013 0.5109 --0.001957 -0.093507 0.542871 --0.009467 -0.099126 0.510991 --0.035298 -0.101612 0.507551 --0.01426 -0.097154 0.458971 --0.004296 -0.064566 0.210009 -0.009517 -0.069399 0.246137 -0.026069 -0.071576 0.257988 -0.174655 0.01451 -0.214808 -0.173821 0.004595 -0.257441 -0.168303 0.024016 -0.256211 -0.175462 -0.015423 -0.258951 -0.1718 -0.00441 -0.293582 -0.163634 0.005286 -0.322165 -0.152804 0.016459 -0.33883 -0.157001 0.021904 -0.321052 -0.147233 0.035817 -0.319925 -0.159648 0.031627 -0.291239 -0.159558 0.041268 -0.255456 -0.146861 0.054797 -0.254858 -0.155792 0.05037 -0.212704 -0.148643 0.058914 -0.169526 -0.162318 0.04321 -0.170186 -0.16849 0.034677 -0.128877 -0.172439 0.024603 -0.171537 -0.179366 0.002286 -0.173456 -0.182242 -0.010652 -0.132087 -0.181041 -0.019273 -0.175061 -0.177474 -0.03964 -0.176264 -0.177971 -0.027068 -0.217924 -0.134343 0.058148 -0.289554 -0.134626 0.04881 -0.318837 -0.119812 0.057273 -0.317494 -0.133323 0.040997 -0.33629 -0.109918 0.05439 -0.334077 -0.10214 0.060962 -0.315465 -0.086957 0.059268 -0.313516 -0.095394 0.067287 -0.28474 -0.085052 0.069002 -0.247709 -0.107071 0.071003 -0.250465 -0.119164 0.072706 -0.21002 -0.128348 0.065555 -0.253251 -0.028509 -0.062365 0.653021 -0.036108 -0.055071 0.641878 -0.04558 -0.05245 0.66701 -0.032152 -0.063137 0.60769 -0.040205 -0.048321 0.630067 -0.0408 -0.050443 0.611472 -0.048401 -0.033136 0.632478 -0.049234 -0.019221 0.623613 -0.054612 -0.014209 0.644259 -0.057802 0.002485 0.64549 -0.060721 -0.005676 0.65792 -0.065591 0.000503 0.663586 -0.067219 -0.011753 0.672911 -0.072041 -0.011396 0.684503 -0.069883 -0.025037 0.68734 -0.071229 -0.034042 0.705776 -0.061825 -0.04057 0.687388 -0.007749 -0.071412 0.681176 -0.017824 -0.070295 0.667247 -0.012474 -0.06725 0.654764 -0.000418 -0.068214 0.64512 --0.011874 -0.069195 0.644369 -0.003307 -0.075928 0.612096 -0.003083 -0.085682 0.577241 -0.018014 -0.079302 0.588959 -0.029115 -0.076531 0.580916 -0.041843 0.031531 -0.054264 -0.046956 0.047724 -0.084097 -0.035804 0.02614 -0.082837 -0.06273 0.065099 -0.084751 -0.052552 0.058425 -0.123083 -0.06228 0.064521 -0.163763 -0.045416 0.048726 -0.162482 -0.040365 0.03884 -0.202978 -0.032626 0.029131 -0.160851 -0.024746 0.009647 -0.160336 -0.028611 0.017247 -0.120191 -0.028682 0.006079 -0.082532 -0.024102 -0.012962 -0.083182 -0.028796 -0.00423 -0.056545 -0.028981 -0.018724 -0.042869 -0.032428 -0.000686 -0.037014 -0.038354 0.015013 -0.034427 -0.044616 0.030651 -0.02668 -0.042996 0.03104 -0.012194 -0.053191 0.043575 -0.011379 -0.061717 0.050002 0.009207 -0.066937 0.059209 -0.016982 -0.073686 0.068823 -0.050037 -0.084616 0.075252 -0.085947 --0.206772 0.026449 0.219402 --0.207281 0.026054 0.196015 --0.217749 0.037713 0.203031 --0.202416 0.011146 0.190098 --0.210607 0.024829 0.169402 --0.217843 0.021974 0.143736 --0.222708 0.036883 0.149652 --0.235334 0.046607 0.132096 --0.233176 0.048541 0.156668 --0.247204 0.054673 0.163415 --0.231543 0.048146 0.183434 --0.231777 0.043844 0.209776 --0.247845 0.043785 0.215521 --0.232266 0.037564 0.231569 --0.231348 0.029005 0.24833 --0.219678 0.028227 0.243814 --0.208003 0.020542 0.256459 --0.208212 0.023241 0.238393 --0.200402 0.013902 0.233699 --0.193367 0.002578 0.248041 --0.195636 0.000164 0.228781 --0.195384 -0.01338 0.226177 --0.197141 -0.002198 0.208566 --0.203681 -0.0054 0.185921 --0.124502 -0.087365 0.436587 --0.11767 -0.024869 0.314524 --0.110453 -0.028499 0.285352 --0.115015 -0.048167 0.327124 -0.13688 -0.018398 -0.453705 -0.145146 -0.023604 -0.458171 -0.14324 -0.013696 -0.469193 -0.152096 -0.012738 -0.486574 -0.141551 -0.005659 -0.486438 -0.139321 1.7e-05 -0.507051 --0.107862 0.067365 -0.955328 --0.110694 0.049862 -0.953401 --0.106635 0.059203 -0.963042 --0.117119 0.043291 -0.944241 --0.115311 0.031862 -0.950026 --0.119732 0.013277 -0.946169 --0.116956 0.023495 -0.95581 --0.121207 0.013384 -0.960631 --0.115206 0.033599 -0.965693 --0.122194 0.042528 -0.973164 --0.109501 0.051305 -0.969549 --0.106108 0.067989 -0.970754 --0.117075 0.077062 -0.975093 --0.10601 0.083071 -0.970754 --0.108153 0.096173 -0.970272 --0.105911 0.089891 -0.964247 --0.112893 0.098274 -0.956293 --0.107236 0.082972 -0.956534 --0.112695 0.073005 -0.946893 --0.118653 0.05789 -0.939902 --0.124689 0.03842 -0.932912 --0.054251 -0.024329 0.570632 --0.111988 -0.029166 0.149335 --0.102827 -0.050105 0.155093 --0.116449 -0.007149 0.142486 --0.109537 -0.085282 0.124806 --0.107266 -0.068478 0.141081 --0.091664 -0.065896 0.159362 --0.101448 -0.006364 0.205763 --0.100792 0.002775 0.176828 --0.095502 0.023304 0.165658 --0.106851 0.009749 0.155037 --0.115526 0.014161 0.134647 --0.109753 0.047412 0.082583 --0.115809 0.052959 0.064582 --0.107196 0.060068 0.03924 --0.12426 0.057162 0.041541 --0.135163 0.05902 0.014473 --0.110525 0.02955 0.124647 --0.097403 0.04055 0.131958 --0.102391 0.040374 0.113425 --0.093323 0.048946 0.102827 --0.102411 0.045252 0.094047 --0.094558 0.04884 0.075356 -0.092157 0.048065 -0.362567 -0.080423 0.052717 -0.331143 -0.090473 0.050157 -0.344858 -0.103947 0.049278 -0.349382 -0.115333 0.043908 -0.360673 -0.102967 0.047707 -0.360461 -0.154261 -0.028668 -0.468845 -0.162366 -0.034723 -0.485764 -0.158569 -0.022198 -0.486355 -0.169657 -0.042323 -0.506075 -0.174619 -0.052305 -0.527479 -0.173675 -0.036642 -0.528507 -0.176606 -0.031142 -0.553545 -0.169928 -0.022748 -0.529261 -0.161807 -0.010156 -0.52955 -0.162113 -0.015904 -0.507419 -0.149061 -0.034016 -0.453482 -0.043372 0.087032 0.607996 -0.050796 0.077545 0.612581 -0.057733 0.0781 0.627504 -0.05621 0.065484 0.618657 -0.060708 0.050856 0.625212 --0.096239 0.027188 0.263362 --0.090507 0.027446 0.23157 --0.099124 0.006927 0.234635 --0.079112 0.04604 0.233137 --0.082624 0.036539 0.199058 --0.073807 0.044645 0.17829 --0.086095 0.035676 0.172444 --0.091055 0.038716 0.152578 --0.104198 -0.015731 0.243364 --0.087677 0.048747 0.283971 --0.076685 0.070177 0.293928 --0.076986 0.059499 0.267173 --0.066782 0.058222 0.233865 --0.219107 0.005427 0.139559 --0.227208 0.019759 0.120406 --0.239844 0.01887 0.100013 --0.238051 0.031753 0.104238 --0.251669 0.043873 0.081306 --0.241312 0.043693 0.110012 --0.249499 0.05209 0.11651 --0.266 0.059433 0.093409 --0.26285 0.057929 0.122881 --0.276557 0.058314 0.126641 --0.262711 0.058578 0.145212 --0.263273 0.054613 0.16916 --0.062151 -0.104074 0.501967 --0.09365 -0.103528 0.496735 --0.120818 -0.100624 0.499053 --0.140169 -0.09861 0.499327 --0.121535 -0.102212 0.471815 --0.120483 -0.100043 0.445758 -0.032502 0.105532 0.635204 -0.040234 0.101373 0.627981 -0.029099 0.103641 0.625923 --0.159727 -0.11605 -0.943517 --0.154575 -0.122851 -0.960149 --0.145729 -0.122779 -0.948098 --0.159244 -0.119119 -0.973887 --0.14056 -0.118146 -0.97678 --0.136031 -0.125444 -0.971237 --0.124119 -0.121526 -0.966898 --0.134307 -0.124716 -0.957739 --0.13451 -0.12082 -0.938938 --0.125296 -0.116442 -0.93725 --0.132554 -0.117879 -0.901824 --0.12834 -0.118523 -0.851712 --0.137255 -0.119545 -0.85154 --0.139205 -0.123264 -0.795694 --0.147709 -0.116859 -0.829962 --0.1577 -0.113684 -0.792993 --0.156937 -0.108369 -0.848223 --0.16437 -0.097422 -0.845784 --0.162056 -0.10353 -0.890525 --0.168228 -0.09362 -0.914112 --0.165287 -0.106934 -0.925923 --0.172012 -0.105366 -0.947133 --0.177922 -0.090558 -0.947133 --0.176295 -0.100282 -0.965211 --0.17577 -0.091291 -0.975334 --0.168679 -0.107233 -0.975334 --0.159577 -0.109299 -0.978226 -0.19361 0.100996 -0.957016 --0.346552 0.051592 -0.138698 --0.05331 0.043614 -0.011406 --0.043116 0.031079 -0.012221 --0.044736 0.030689 -0.026708 --0.036458 0.035374 -0.005666 --0.034593 0.02256 -0.016369 --0.026002 0.020196 -0.027749 --0.032548 -0.000647 -0.037041 --0.038473 0.015053 -0.034454 --0.042106 0.031463 -0.054643 --0.036354 0.025857 -0.083922 --0.047462 0.04744 -0.085184 --0.053157 0.058033 -0.124522 --0.063386 0.064816 -0.085837 --0.085277 0.075076 -0.086681 --0.074292 0.068754 -0.050417 --0.086456 0.067805 -0.017768 --0.067251 0.059248 -0.01701 --0.061836 0.050042 0.009179 --0.072076 0.051032 0.033361 --0.063817 0.04906 0.040277 --0.070173 0.052519 0.067033 --0.056287 0.052694 0.047223 --0.043826 0.062622 0.049801 --0.041395 0.049328 0.017863 --0.026846 0.045526 -0.009819 -0.026111 -0.038364 -0.058814 -0.02288 -0.033403 -0.084919 -0.027925 -0.055292 -0.087379 -0.02033 -0.022247 -0.121654 -0.021046 -0.010807 -0.16115 -0.022959 -0.030884 -0.1634 -0.029038 -0.037487 -0.206713 -0.03026 -0.050994 -0.166212 -0.032675 -0.064246 -0.126926 -0.035877 -0.074808 -0.088427 -0.048094 -0.089411 -0.089855 -0.040517 -0.087569 -0.059881 -0.051296 -0.100298 -0.037443 -0.035125 -0.08673 -0.04151 -0.028149 -0.086287 -0.034555 -0.020779 -0.046423 -0.035166 -0.025827 -0.040847 -0.041556 --5.2e-05 -0.062188 0.00548 -0.006463 -0.067002 -0.005033 -0.012015 -0.073985 -0.017958 -0.011292 -0.086761 -0.001085 -0.019728 -0.107513 0.001326 --0.351987 0.047534 -0.115422 --0.14288 0.119413 -0.96321 --0.164423 0.119848 -0.961836 --0.124308 0.111749 -0.956052 --0.124703 0.115167 -0.964728 --0.131241 0.115405 -0.971237 --0.146413 0.109641 -0.974611 --0.154631 0.117995 -0.971237 --0.177461 0.114153 -0.9722 --0.183398 0.103486 -0.976057 --0.196445 0.10383 -0.971959 --0.209582 0.088665 -0.970754 --0.205996 0.096639 -0.964969 --0.206554 0.088005 -0.954943 --0.198745 0.100946 -0.957016 --0.186213 0.111705 -0.961681 --0.101613 0.044489 0.508518 --0.091777 0.062148 0.497244 --0.115934 0.045627 0.502282 --0.065327 0.068468 0.494147 --0.080758 0.081006 0.485764 --0.072828 0.096906 0.474952 --0.094039 0.092566 0.477121 --0.105515 0.105185 0.467462 --0.114767 0.087024 0.475675 --0.128024 0.080515 0.470854 --0.129064 0.06286 0.483867 --0.141192 0.04135 0.481454 --0.131264 0.042973 0.496583 --0.086956 0.0366 0.517973 --0.077142 0.031594 0.526258 --0.062917 0.040509 0.517528 --0.042954 0.041937 0.516578 --0.051125 0.05472 0.504975 --0.037893 0.067004 0.494885 -0.093484 -0.097607 0.529183 -0.073719 -0.095196 0.538101 -0.094082 -0.086854 0.544358 -0.063594 -0.088335 0.548857 -0.062627 -0.07834 0.556719 -0.043104 -0.080268 0.563468 -0.034656 -0.067018 0.583958 --0.159365 0.009212 -0.972683 --0.138816 0.025941 -0.973647 --0.141228 -0.000439 -0.968586 --0.138367 0.050982 -0.977021 --0.124538 -0.006661 -0.956534 --0.130607 -0.004067 -0.963041 --0.129136 -0.034829 -0.963282 --0.136905 -0.046366 -0.96979 --0.13199 -0.067156 -0.975575 --0.148774 -0.059115 -0.97437 --0.161709 -0.072079 -0.977504 --0.16425 -0.051076 -0.974611 --0.175649 -0.042919 -0.972683 --0.186474 -0.009328 -0.973165 --0.175349 -0.002784 -0.973887 --0.183278 0.02446 -0.975575 --0.203258 0.038845 -0.975816 --0.188311 0.049937 -0.977263 --0.188748 0.069427 -0.977986 --0.16395 0.05973 -0.977745 --0.136642 0.068833 -0.977745 -0.020641 0.106222 0.68288 -0.010145 0.114881 0.68217 --0.029 -0.004162 0.808506 --0.018674 -0.009285 0.810771 --0.027318 0.00999 0.810749 --0.009358 -0.027252 0.807329 --0.009189 -0.013674 0.811803 -0.000182 -0.009914 0.812958 --0.008739 -0.00082 0.81441 --0.026137 0.022133 0.811998 --0.034725 0.029576 0.807259 --0.036296 0.04573 0.803895 --0.043479 0.035008 0.801776 --0.050999 0.040011 0.794549 --0.046173 0.020569 0.800655 --0.049765 0.005415 0.797041 --0.039102 0.000983 0.803895 --0.030572 -0.017744 0.803895 --0.033041 -0.031611 0.797041 --0.021032 -0.035161 0.800678 --0.010705 -0.050268 0.794618 --0.010144 -0.039546 0.801847 -0.000295 -0.037069 0.804011 --0.129301 0.086215 -0.97678 --0.133015 -0.094692 0.454036 --0.160025 -0.094814 0.509594 --0.154706 -0.098454 0.492353 --0.167264 -0.097792 0.475314 --0.148605 -0.096707 0.473117 --0.143549 -0.091627 0.451185 --0.072193 0.039441 0.677409 --0.069623 0.032875 0.660902 --0.072019 0.033374 0.670516 --0.073593 0.030982 0.680927 --0.074948 0.036613 0.688621 --0.071575 0.047963 0.690054 --0.070167 0.058917 0.70771 --0.068208 0.061066 0.690866 --0.063995 0.074404 0.689656 --0.065714 0.069965 0.676427 --0.063559 0.081997 0.666975 --0.065384 0.069769 0.659252 --0.062969 0.074671 0.643834 --0.066039 0.05651 0.649292 --0.065441 0.044988 0.639524 --0.067935 0.038771 0.651999 --0.067598 0.025852 0.654161 -0.006898 0.113222 0.666528 -0.019509 0.109191 0.657762 -0.012014 0.113397 0.650796 --0.346918 0.12142 -0.117927 --0.354088 0.121599 -0.124121 --0.344419 0.115564 -0.103046 --0.34972 0.117279 -0.110488 -0.127701 -0.017526 -0.914112 -0.134572 -0.007901 -0.908327 -0.143269 -0.019543 -0.89748 -0.143442 -0.032239 -0.887839 -0.151011 -0.035966 -0.894347 -0.156411 -0.049333 -0.89507 -0.158344 -0.037714 -0.906158 -0.165572 -0.036508 -0.92544 --0.02225 0.059766 0.501427 --0.023436 0.07472 0.489154 --0.011365 0.079551 0.484593 --0.024381 0.086541 0.474952 --0.015704 0.092085 0.464828 --0.029443 0.098834 0.459284 --0.029254 0.111213 0.441758 --0.039324 0.10751 0.457116 --0.050653 0.119321 0.4515 --0.054991 0.107029 0.465552 --0.0709 0.112091 0.464033 -0.020423 0.102461 0.608305 -0.035992 0.09389 0.606934 -0.025793 0.095655 0.598077 -0.051857 0.097225 0.633387 -0.04315 0.095805 0.617906 --0.181442 -0.053912 -0.970754 --0.185342 -0.0355 -0.968585 --0.188407 -0.024348 -0.958462 --0.192056 -0.015446 -0.969067 --0.200678 0.00534 -0.970995 --0.205676 0.015018 -0.963765 --0.209533 0.02627 -0.97196 --0.215892 0.046993 -0.970995 --0.020731 -0.081957 -0.028567 --0.015762 -0.05829 -0.029114 --0.012329 -0.073985 -0.017958 --0.020393 -0.046404 -0.035179 --0.012687 -0.019453 -0.035064 --0.011094 -0.036464 -0.029315 --5.2e-05 -0.043146 -0.02248 --0.006793 -0.067002 -0.005033 --0.011606 -0.086761 -0.001326 --0.01016 -0.099053 0.016992 --0.019801 -0.107513 0.000844 --0.03519 -0.122715 0.002285 --0.031337 -0.113581 -0.011936 --0.048416 -0.114586 -0.018931 --0.031544 -0.100799 -0.024715 --0.028404 -0.086268 -0.034568 --0.035628 -0.086704 -0.041528 --0.02362 -0.066445 -0.03714 --0.025699 -0.040813 -0.041578 --0.217927 0.056178 -0.96256 --0.216728 0.068043 -0.96979 --0.204529 0.079384 -0.975334 --0.187076 0.088261 -0.978226 --0.164926 0.099509 -0.977263 --0.140782 0.098259 -0.976539 --0.12071 0.098647 -0.974129 --0.015554 0.023029 0.566772 --0.019233 0.031936 0.534362 --0.006303 0.039781 0.519301 --0.017401 0.043994 0.515556 --0.043958 0.004853 0.609394 --0.051897 0.010736 0.631519 --0.043078 0.012732 0.61846 --0.045175 0.024826 0.613791 --0.029703 0.021295 0.609428 --0.015003 0.027943 0.601931 --0.015397 0.021991 0.592477 --0.005867 0.023916 0.567498 --0.097654 0.15403 0.414501 --0.106021 0.1509 0.422436 --0.114099 0.14311 0.431965 --0.105181 0.145039 0.431965 --0.09795 0.134674 0.443848 --0.094669 0.144955 0.431429 --0.080782 0.140532 0.434579 --0.089942 0.14945 0.421973 --0.084901 0.150248 0.411978 --0.068533 0.142851 0.413384 --0.079138 0.149118 0.400054 --0.068103 0.145349 0.392723 --0.08706 0.150934 0.389497 --0.094948 0.154109 0.402241 --0.104885 0.155597 0.405945 --0.123565 0.146136 0.401605 --0.113336 0.154226 0.409681 --0.122239 0.148005 0.414019 --0.081095 0.052591 0.072127 --0.082705 0.052338 0.053784 --0.089286 0.058018 0.03567 --0.098251 0.065585 0.010731 -0.28925 0.045602 -0.009567 -0.292529 0.034588 -0.011436 -0.284719 0.030641 0.007874 -0.298173 0.032736 -0.023181 -0.302759 0.030528 -0.010877 -0.312972 0.037178 -0.005232 -0.301493 0.022318 0.010571 -0.296961 0.01204 0.043347 -0.286506 0.011417 0.039962 -0.270972 0.000318 0.073557 -0.278498 0.015939 0.038776 -0.271804 0.025941 0.039055 -0.254717 0.020693 0.073311 -0.269021 0.037206 0.041245 -0.267648 0.047465 0.045042 -0.280242 0.053479 0.012775 -0.288229 0.065783 -0.000674 -0.287556 0.057176 -0.006856 -0.289871 0.057957 -0.019069 -0.295948 0.074193 -0.034474 -0.293578 0.055729 -0.035571 -0.299659 0.054995 -0.052561 -0.298705 0.042921 -0.044444 -0.310441 0.034345 -0.058644 -0.305132 0.033182 -0.038279 -0.235805 -0.050145 0.243698 -0.245376 -0.048187 0.236911 -0.246224 -0.046905 0.24834 -0.242491 -0.046658 0.225826 -0.255 -0.04119 0.231842 -0.266692 -0.032272 0.222715 -0.26212 -0.034758 0.239813 -0.263623 -0.029623 0.251835 -0.256126 -0.03921 0.250514 -0.246606 -0.046318 0.264945 -0.245716 -0.046049 0.285837 -0.236221 -0.052131 0.281348 -0.22488 -0.058924 0.302204 -0.225541 -0.054724 0.276795 -0.214081 -0.053728 0.27235 -0.225508 -0.052151 0.25575 -0.224536 -0.049797 0.239557 -0.214015 -0.045365 0.236305 -0.221176 -0.04779 0.228147 -0.216208 -0.041047 0.217972 -0.229561 -0.046609 0.221203 -0.240155 -0.043871 0.209954 --0.059417 0.009418 0.787695 --0.053243 -0.009739 0.790062 --0.05706 -0.024749 0.778846 --0.045051 -0.028037 0.790062 --0.035286 -0.044335 0.787695 --0.180683 -0.041219 -0.578831 --0.175537 -0.031146 -0.553571 --0.176602 -0.025983 -0.580031 --0.172605 -0.036647 -0.528534 --0.168859 -0.022753 -0.529288 --0.161242 -0.015909 -0.507445 --0.160737 -0.010161 -0.529577 --0.14849 -0.00131 -0.529345 --0.159038 -0.005362 -0.554497 --0.154699 -0.003267 -0.581093 --0.168224 -0.012792 -0.580668 --0.175483 -0.022229 -0.612273 --0.169777 -0.021693 -0.649493 --0.17872 -0.03387 -0.646851 --0.179299 -0.047129 -0.689553 --0.183473 -0.048939 -0.644727 --0.185221 -0.064888 -0.643884 --0.184318 -0.052534 -0.60895 --0.181709 -0.057628 -0.577707 --0.179005 -0.07687 -0.577528 --0.177507 -0.063217 -0.551419 --0.170991 -0.068748 -0.52619 --0.173551 -0.05231 -0.527506 --0.168785 -0.042328 -0.506102 -0.092154 -0.05311 0.557856 -0.110348 -0.062122 0.551578 -0.118544 -0.049829 0.550614 -0.091429 -0.071075 0.555353 -0.112759 -0.07562 0.547722 -0.124087 -0.083092 0.539767 -0.123488 -0.090952 0.530619 -0.147208 -0.082727 0.532732 -0.163751 -0.086596 0.523111 -0.167215 -0.074061 0.532267 -0.181355 -0.068716 0.527983 -0.169725 -0.059203 0.536731 -0.16996 -0.044296 0.538715 -0.157488 -0.047446 0.542037 -0.139754 -0.03396 0.544878 -0.138367 -0.044594 0.546776 -0.117945 -0.037202 0.550143 -0.109874 -0.02361 0.549189 -0.093007 -0.030242 0.555939 -0.070108 -0.028687 0.562213 -0.069634 -0.03808 0.564859 -0.054196 -0.045559 0.577154 -0.068181 -0.04832 0.566305 -0.066004 -0.057483 0.565488 -0.064556 -0.067734 0.563709 -0.140035 -0.093514 -0.092145 -0.148605 -0.086131 -0.134493 -0.157508 -0.080765 -0.09277 -0.155489 -0.074654 -0.177276 -0.169056 -0.0578 -0.17712 -0.175859 -0.05251 -0.134745 -0.178984 -0.044802 -0.092636 -0.171196 -0.063998 -0.093178 -0.162918 -0.075809 -0.054259 -0.162813 -0.069618 -0.0175 -0.15164 -0.088465 -0.018245 -0.141603 -0.099259 0.009952 -0.13688 -0.101658 -0.018535 -0.1209 -0.109584 -0.023743 -0.130686 -0.101752 -0.053921 -0.117814 -0.101416 -0.091734 -0.091184 -0.103428 -0.091451 -0.019782 0.089341 0.584488 --0.056232 0.093284 0.679412 --0.072636 0.057242 0.724416 --0.066049 0.072554 0.719436 --0.059213 0.086421 0.724422 --0.059279 0.085457 0.712483 --0.051306 0.093238 0.699401 --0.058462 0.085324 0.699207 --0.047663 0.097322 0.687755 -0.005499 0.057805 -0.010951 -0.012745 0.065075 0.010335 -0.015692 0.053492 -0.012164 -0.007199 0.074492 0.042059 -0.026725 0.070387 0.044149 -0.042585 0.072901 0.084773 -0.044231 0.062609 0.049328 -0.056432 0.052674 0.046995 -0.041781 0.04931 0.017394 -0.036587 0.035342 -0.005884 -0.027221 0.045504 -0.010286 -0.018781 0.04154 -0.022529 -0.019554 0.029015 -0.029508 -0.012607 0.034629 -0.03064 --6.1e-05 0.037879 -0.031299 --6.3e-05 0.049097 -0.021972 --0.005372 0.057808 -0.010711 --0.015317 0.053505 -0.011692 --0.01235 0.065084 0.010811 --0.026311 0.0704 0.044621 --0.007026 0.074499 0.042295 --3e-05 0.078582 0.077556 -0.034475 0.02252 -0.016341 -0.027855 0.006828 -0.029477 -0.025882 0.020158 -0.027721 -0.070069 0.052493 0.067051 -0.063705 0.049027 0.040299 -0.1708 -0.048786 -0.016719 -0.178692 -0.035781 -0.052935 -0.15983 -0.064509 0.01593 -0.181799 -0.02331 -0.091181 -0.179477 7.1e-05 -0.089955 -0.172651 0.023084 -0.088665 -0.151966 -0.0452 0.070189 -0.15501 -0.056338 0.04692 -0.149717 -0.077178 0.043467 --0.060997 0.014244 0.640455 -0.029437 0.020503 0.56901 -0.036427 0.012307 0.596246 -0.027474 0.026888 0.545481 -0.022206 0.024118 0.555512 -0.014975 0.022983 0.566758 -0.005541 0.023894 0.567491 -0.051751 0.010618 0.631502 -0.042587 0.012608 0.618431 -0.04362 0.004713 0.609833 -0.047187 -0.006745 0.609202 -0.044487 0.007977 0.591049 -0.04478 0.004342 0.572568 -0.039869 0.016341 0.56975 -0.035477 0.023909 0.55068 -0.231228 0.028966 0.248357 -0.232147 0.037525 0.231595 -0.219558 0.028188 0.243842 -0.247727 0.043746 0.215549 -0.231658 0.043805 0.209803 -0.231425 0.048107 0.183461 -0.217631 0.037675 0.203057 -0.207163 0.026016 0.196042 -0.206654 0.026409 0.219429 -0.200283 0.013862 0.233726 -0.208092 0.023203 0.238421 -0.207884 0.020504 0.256487 -0.197155 0.012116 0.273302 -0.206211 0.017357 0.277512 -0.203911 0.01663 0.302668 -0.21645 0.019107 0.281786 -0.22791 0.018111 0.286231 -0.229794 0.023032 0.265276 -0.244364 0.024711 0.252074 -0.25506 0.017995 0.254749 -0.260255 0.027033 0.238936 -0.274955 0.025766 0.220281 -0.262699 0.037508 0.219172 -0.263562 0.047989 0.194952 --0.055273 -0.045936 -0.519443 --0.057572 -0.052139 -0.497425 --0.056619 -0.062531 -0.518394 --0.061513 -0.042844 -0.478046 --0.062629 -0.056837 -0.477045 --0.067662 -0.05984 -0.460489 --0.06743 -0.071226 -0.476496 --0.075061 -0.082434 -0.476421 --0.067654 -0.082092 -0.4964 --0.071384 -0.093884 -0.51898 --0.0619 -0.078097 -0.517919 --0.057834 -0.072877 -0.544067 --0.062274 -0.085678 -0.573733 --0.056388 -0.067092 -0.573757 --0.059578 -0.062913 -0.61318 --0.054796 -0.050683 -0.574783 --0.057039 -0.034769 -0.576001 --0.054895 -0.039873 -0.546142 --0.057761 -0.030459 -0.520797 --0.064086 -0.016652 -0.522259 --0.0623 -0.022607 -0.50001 --0.068868 -0.018314 -0.480453 --0.063753 -0.02879 -0.479277 --0.065459 -0.034827 -0.462233 --0.109923 0.097139 0.355728 --0.090865 0.091656 0.35474 --0.071542 0.095326 0.351671 --0.056188 0.093387 0.339369 --0.077145 0.084673 0.334731 --0.079236 0.078124 0.313693 --0.100936 -0.085951 -0.260342 --0.120891 -0.085269 -0.26252 --0.108267 -0.077994 -0.29988 --0.131186 -0.08996 -0.220553 --0.137402 -0.07889 -0.262806 --0.152178 -0.065241 -0.261914 --0.141676 -0.067763 -0.300371 --0.143641 -0.056073 -0.33192 --0.129503 -0.065865 -0.335172 --0.121419 -0.06514 -0.367938 --0.112603 -0.069717 -0.334966 --0.095862 -0.069837 -0.333635 --0.091719 -0.066681 -0.364809 --0.079744 -0.06832 -0.330753 --0.066743 -0.06431 -0.326443 --0.072427 -0.072997 -0.293351 --0.062856 -0.075002 -0.253308 --0.081215 -0.082472 -0.256694 --0.088531 -0.093675 -0.217257 --0.074539 -0.096574 -0.174349 --0.097136 -0.100976 -0.176111 --0.106882 -0.103202 -0.134252 --0.119835 -0.097944 -0.177229 --0.139993 -0.088744 -0.177323 -0.018239 0.107256 0.671512 --0.063667 -0.030882 -0.613891 --0.063363 -0.02034 -0.57735 --0.07406 -0.008564 -0.578235 --0.067545 -0.011638 -0.548953 --0.073747 -0.006272 -0.523751 --0.08742 0.000865 -0.525118 --0.080661 -0.003753 -0.502829 --0.085059 -0.001355 -0.479605 --0.077569 -0.009841 -0.481781 --0.076309 -0.016679 -0.464386 -0.000655 0.093766 0.458566 --0.002688 0.085095 0.474469 -0.01032 0.079544 0.484598 -0.022375 0.074701 0.489167 -0.023779 0.086522 0.474965 -0.029284 0.098801 0.459307 -0.016284 0.094475 0.458816 -0.003999 0.097856 0.43977 -0.008865 0.104204 0.421525 --0.001514 0.098828 0.420243 --0.002447 0.094977 0.395652 --0.010454 0.103501 0.414762 --0.019698 0.112569 0.404978 --0.021112 0.113229 0.423026 --0.034872 0.124633 0.422118 --0.108977 -0.07772 -0.938697 --0.109259 -0.095338 -0.938938 --0.107241 -0.088549 -0.953641 --0.113633 -0.098839 -0.923753 --0.116069 -0.109289 -0.937974 --0.115738 -0.112744 -0.967861 --0.120446 -0.104066 -0.975093 --0.111521 -0.09868 -0.966175 --0.110599 -0.079589 -0.965452 --0.118576 -0.073285 -0.973405 --0.114012 -0.059077 -0.964005 --0.119658 -0.041412 -0.959667 --0.117299 -0.048889 -0.948579 --0.122066 -0.040734 -0.934599 --0.115623 -0.05857 -0.935082 --0.113042 -0.071006 -0.923031 --0.116876 -0.070528 -0.907364 --0.114063 -0.084261 -0.914594 --0.114361 -0.096332 -0.891874 -0.092611 -0.019001 0.551748 -0.121772 -0.017925 0.544638 -0.115293 -0.000121 0.538382 -0.097639 0.005006 0.540291 -0.071429 0.018691 0.538825 -0.0619 0.007477 0.546542 -0.052729 -0.000966 0.55956 -0.070088 -0.007715 0.552091 -0.070817 -0.020132 0.558359 -0.054188 -0.023873 0.571133 --0.138607 0.02768 -0.35295 --0.145511 0.020286 -0.352691 --0.152296 0.016528 -0.338967 --0.143987 0.017902 -0.368736 --0.136642 0.01861 -0.394138 --0.143563 0.00789 -0.390521 --0.137355 0.00316 -0.416302 --0.146884 -0.00545 -0.387504 --0.144468 -0.015412 -0.390165 --0.15644 -0.015421 -0.353648 --0.161971 -0.027536 -0.325287 --0.165474 -0.010869 -0.323954 --0.17139 -0.004353 -0.293696 --0.163127 0.005355 -0.322302 --0.156493 0.021972 -0.321189 --0.159237 0.031684 -0.291354 --0.14683 0.03588 -0.320043 --0.134195 0.048864 -0.318919 --0.133129 0.041051 -0.336372 --0.119072 0.0442 -0.348832 --0.130775 0.036182 -0.351647 --0.126896 0.038675 -0.36124 --0.077792 -0.020144 -0.448556 --0.069431 -0.035049 -0.448115 --0.326873 0.070892 -0.095212 --0.323667 0.076354 -0.088293 --0.325844 0.086822 -0.08913 --0.303517 0.08504 -0.050481 --0.305062 0.071913 -0.060507 --0.298297 0.05325 -0.051365 --0.335226 0.071435 -0.105031 --0.336521 0.091323 -0.101414 -0.095477 0.114936 0.354458 -0.074906 0.108128 0.353034 -0.083691 0.118559 0.354297 -0.055065 0.107161 0.357513 -0.036546 0.108676 0.369588 -0.052728 0.116812 0.361744 -0.062169 0.129533 0.366115 -0.070881 0.123564 0.35888 -0.088753 0.131816 0.363311 -0.090854 0.143024 0.374301 -0.100805 0.141939 0.376978 -0.109457 0.13928 0.37762 -0.118765 0.132885 0.374621 -0.109192 0.125534 0.364114 -0.109846 0.110711 0.356521 -0.12183 0.107697 0.360592 -0.109803 0.0971 0.355756 -0.113684 0.08144 0.361935 -0.090746 0.091617 0.354768 -0.071427 0.095289 0.351696 -0.139244 -0.091472 0.060615 -0.142666 -0.093081 0.038234 -0.130994 -0.108076 0.032687 --0.01243 0.098552 0.594381 --0.025239 0.095565 0.597988 --0.075025 0.108167 0.353006 --0.055184 0.107199 0.357485 --0.052847 0.11685 0.361717 --0.036657 0.108709 0.369566 --0.025423 0.111393 0.38572 --0.024164 0.101644 0.37025 --0.014121 0.093803 0.35882 --0.031916 0.096268 0.349002 --0.04652 0.092123 0.322665 --0.040688 0.086532 0.306737 --0.061795 0.083175 0.305975 --0.000933 0.105174 0.600462 --0.035943 -0.066474 0.205451 --0.0186 -0.064312 0.200394 --0.08577 -0.092697 -0.476498 --0.097479 -0.099416 -0.476688 --0.090447 -0.106019 -0.497618 --0.101793 -0.091054 -0.460571 --0.110883 -0.10191 -0.477474 --0.123514 -0.099711 -0.478696 --0.118719 -0.11206 -0.49993 --0.128373 -0.119869 -0.525079 --0.112883 -0.122273 -0.52364 --0.106856 -0.129589 -0.550984 --0.098014 -0.118725 -0.522114 --0.083467 -0.107549 -0.52012 --0.077161 -0.107657 -0.546619 --0.082954 -0.081004 -0.460122 --0.081541 -0.070322 -0.446183 --0.092255 -0.079879 -0.45023 --0.104304 -0.079581 -0.445782 --0.155674 -0.074622 -0.177299 --0.16916 -0.057773 -0.177139 --0.161643 -0.062189 -0.219637 --0.16425 -0.050033 -0.260919 -0.127133 0.121437 0.370812 -0.139715 0.083369 0.375361 -0.134388 0.103125 0.368146 --0.068844 0.012849 0.774859 --0.063682 0.02886 0.78246 --0.050887 0.056595 0.78944 --0.058855 0.0443 0.785701 --0.064074 0.048032 0.775737 --0.060763 0.065345 0.769511 --0.069069 0.051193 0.763654 --0.071972 0.053638 0.751696 --0.076084 0.035166 0.755922 --0.080451 0.020839 0.742337 --0.076253 0.015994 0.760403 --0.07311 -0.002734 0.762646 --0.075242 -0.016457 0.745698 --0.067498 -0.021175 0.764266 --0.059978 -0.038044 0.764266 --0.009606 0.112575 0.67871 --0.015597 0.118906 0.696268 --0.00595 0.117643 0.685904 -0.226135 -0.009565 0.137964 -0.240308 -0.009492 0.116998 -0.238392 -0.021308 0.139073 -0.245799 0.004574 0.098227 -0.25536 -0.0058 0.098753 -0.268712 -0.011363 0.101984 -0.281644 -0.011411 0.106607 -0.268632 -0.021294 0.123851 -0.269432 -0.027605 0.148441 -0.253363 -0.027546 0.142696 -0.237528 -0.031788 0.163293 -0.237937 -0.038374 0.189058 -0.222965 -0.032136 0.185436 -0.211831 -0.032069 0.2031 -0.210709 -0.020394 0.184326 -0.203562 -0.00544 0.185949 -0.213017 -0.008303 0.161075 -0.218989 0.005389 0.139586 -0.217724 0.021935 0.143763 -0.227089 0.01972 0.120432 -0.237932 0.031715 0.104265 -0.239727 0.018832 0.100041 -0.130115 0.072367 -0.087024 -0.108208 0.07774 -0.08661 -0.095777 0.07914 -0.125732 --0.345718 0.120033 -0.130569 --0.3545 0.113702 -0.130478 -0.162066 0.04364 -0.087898 -0.14782 0.060596 -0.087193 -0.014973 -0.081226 0.124942 -0.1861 -0.077551 0.515917 -0.193564 -0.061189 0.519585 -0.202622 -0.050908 0.506478 -0.194808 -0.044467 0.52252 -0.189091 -0.027347 0.524711 -0.182835 -0.037088 0.532901 -0.171853 -0.028443 0.534873 -0.159701 -0.094919 0.51001 -0.175026 -0.088818 0.512794 -0.18688 -0.085619 0.498698 -0.112989 -0.099264 0.514738 -0.119816 -0.10079 0.499372 -0.12111 -0.102352 0.472425 -0.139744 -0.098748 0.499938 -0.154484 -0.098526 0.492574 --0.057988 0.086278 0.737506 --0.05054 0.098287 0.724297 --0.040337 0.109009 0.725419 --0.04241 0.106365 0.71133 --0.034774 0.10754 0.698319 --0.043208 0.101822 0.699208 --0.038916 0.101242 0.690966 --0.037643 -0.056629 0.774859 --0.024399 -0.058749 0.782484 --0.012425 -0.070703 0.772788 --0.011827 -0.061132 0.78577 -0.000295 -0.058512 0.789555 --0.060763 -0.048909 0.745698 --0.050326 -0.053627 0.762646 --0.039102 -0.066493 0.760403 -0.059026 0.08642 0.737646 -0.06025 0.086563 0.724562 -0.067086 0.072696 0.719576 -0.060317 0.085599 0.712624 -0.040503 0.101825 0.693342 -0.032757 0.103531 0.684267 --0.0291 -0.018684 -0.042895 --0.02906 -0.004299 -0.056925 --0.026374 -0.038433 -0.059194 --0.028475 -0.055575 -0.088465 --0.02343 -0.033688 -0.086005 --0.020775 -0.022888 -0.123089 --0.024652 -0.013246 -0.084268 --0.029232 0.005796 -0.083617 --0.029056 0.016608 -0.121626 --0.015647 0.006899 -0.037608 --0.017185 -0.003649 -0.036607 --0.008532 -0.002853 -0.039356 --0.058424 0.097628 0.648 --0.054499 0.096907 0.633642 --0.059248 0.088844 0.638419 --0.05795 0.078018 0.627512 --0.007443 -0.071435 0.681152 --0.116739 -0.136397 -0.580844 --0.100306 -0.131872 -0.579544 --0.097219 -0.130442 -0.616268 --0.085158 -0.121117 -0.577172 --0.072117 -0.104671 -0.575223 --0.342027 0.053983 -0.116465 --0.062924 0.055834 0.198102 --0.06222 0.057004 0.16879 --0.062482 0.064178 0.147523 --0.056597 0.070898 0.121654 --0.074757 0.060879 0.130228 --0.084062 0.055142 0.11443 --0.086826 0.049633 0.133312 --0.050465 0.066241 0.237 --0.036428 0.078589 0.278784 -0.130528 0.070492 -0.168451 -0.140569 0.067337 -0.127511 -0.107643 0.076923 -0.167086 -0.083619 0.075084 -0.165215 -0.073121 0.068867 -0.205393 -0.105061 0.144999 0.431993 -0.113979 0.143071 0.431993 -0.118559 0.131743 0.442671 --0.010837 0.077735 0.109421 -0.01077 0.077721 0.10943 -0.022527 0.074634 0.135932 -0.03386 0.076146 0.114557 -0.056505 0.070872 0.121672 --0.107441 -0.101702 0.109968 --0.099315 -0.114582 0.096708 --0.089676 -0.125385 0.083379 --0.069075 -0.050206 -0.446984 --0.073878 -0.060366 -0.446474 --0.074237 -0.059456 -0.424272 --0.03394 0.076172 0.114539 --0.042417 0.072921 0.085 --0.014258 -0.11014 0.033141 --0.157132 -0.090422 0.430384 --0.140529 -0.081659 0.429758 --0.145454 0.048158 0.463138 --0.017373 -0.070336 0.667195 --0.127703 -0.087862 -0.461567 --0.13624 -0.094361 -0.479914 --0.146737 -0.087653 -0.48091 --0.145564 -0.100101 -0.501844 --0.155102 -0.101739 -0.525344 --0.142492 -0.11326 -0.525774 --0.138801 -0.12618 -0.553256 --0.148871 -0.128621 -0.581038 --0.133194 -0.135391 -0.581677 --0.127663 -0.138176 -0.616978 --0.150984 -0.068803 0.390182 --0.078496 0.031985 0.709579 --0.080563 0.025413 0.7244 --0.077425 0.041899 0.723785 --0.073202 0.055797 0.737495 --0.06539 0.070966 0.745601 --0.056146 0.084691 0.751708 --0.018935 0.105176 0.619158 --0.007126 0.109562 0.629151 --0.051168 0.076338 0.775737 --0.054422 0.081515 0.76366 --0.046346 0.095969 0.755939 --0.034454 0.104531 0.76042 --0.037984 0.108279 0.742859 --0.029419 0.116585 0.725169 --0.047298 0.024671 -0.310259 --0.037849 0.017816 -0.27972 --0.040346 0.008987 -0.311486 --0.038052 0.027864 -0.243939 --0.030119 0.00961 -0.244461 --0.02382 4.2e-05 -0.203348 --0.027026 -0.008049 -0.245536 --0.029351 -0.026807 -0.247007 --0.031831 -0.016508 -0.282364 --0.038859 -0.023333 -0.315602 --0.037421 -0.00768 -0.313056 --0.044946 0.004605 -0.332387 --0.053967 0.004087 -0.358133 --0.054656 0.017964 -0.342715 --0.065584 0.033024 -0.344384 --0.056152 0.029739 -0.330176 --0.058364 0.04014 -0.310503 --0.070722 0.05147 -0.311389 --0.060798 0.050331 -0.280418 --0.06604 0.059635 -0.245537 --0.049933 0.044911 -0.244394 --0.039882 0.038028 -0.203344 --0.038989 -0.073927 0.742832 --0.026679 -0.077612 0.752926 --0.013508 -0.085225 0.741744 --0.013247 -0.079702 0.757686 -0.000222 -0.077653 0.76359 --0.123761 -0.022861 -0.937492 --0.128272 -0.030546 -0.92279 --0.132998 -0.017576 -0.914112 --0.133664 -0.033246 -0.905194 --0.132777 -0.045326 -0.894106 --0.124311 -0.054024 -0.87316 --0.124777 -0.059983 -0.896517 --0.11425 -0.070799 -0.880902 --0.105875 -0.070266 -0.84386 --0.105368 -0.07996 -0.844737 --0.09669 -0.083735 -0.796708 --0.108292 -0.09176 -0.848477 --0.114245 -0.103894 -0.85013 --0.075698 -0.022685 -0.438931 --0.066752 -0.020972 -0.416209 --0.093239 0.007916 -0.466902 --0.086006 0.003262 -0.460468 --0.079904 0.004909 -0.444729 --0.07522 -0.005856 -0.439366 --0.066779 0.011585 -0.400071 --0.061683 0.002995 -0.386192 --0.058576 -0.009739 -0.386374 --0.060099 -0.025929 -0.40223 --0.214472 0.06587 -0.955087 --0.211744 0.045888 -0.955087 --0.205851 0.025451 -0.955328 -0.210488 0.024791 0.16943 -0.202298 0.011107 0.190126 -0.197022 -0.002238 0.208593 -0.195264 -0.013418 0.226204 -0.195517 0.000125 0.228806 -0.193247 0.002539 0.248068 -0.185983 -0.008188 0.266481 -0.190326 0.003492 0.269578 -0.184997 0.005073 0.293798 -0.06669 0.059844 -0.245514 -0.0619 0.051035 -0.280406 -0.050798 0.045614 -0.24438 -0.039176 0.029063 -0.243934 --0.00915 0.088447 0.337107 --0.005564 0.08412 0.313582 --0.021113 0.08532 0.31136 -0.355453 0.089966 -0.127158 -0.350899 0.086456 -0.117554 -0.350983 0.097265 -0.137094 -0.072647 0.03954 0.677462 -0.072418 0.04809 0.690165 -0.075207 0.036698 0.688642 -0.073504 0.031041 0.680896 --0.178961 -0.090729 -0.609801 --0.172446 -0.097083 -0.578269 --0.162075 -0.115543 -0.579879 --0.164614 -0.10158 -0.551666 --0.164655 -0.086899 -0.525697 --0.163951 -0.073973 -0.503455 --0.155571 -0.076365 -0.482167 --0.160899 -0.062998 -0.48358 --0.157142 -0.052747 -0.467235 --0.163173 -0.047792 -0.48491 --0.161891 -0.034728 -0.485791 --0.06518 -0.059918 -0.358386 --0.054486 -0.054171 -0.322075 --0.045494 -0.039634 -0.317862 --0.046159 -0.051222 -0.286305 --0.037043 -0.045221 -0.248688 --0.048844 -0.063367 -0.25063 --0.051682 -0.075463 -0.211308 --0.041886 -0.070545 -0.169579 --0.056049 -0.085832 -0.171786 --0.060066 -0.09468 -0.131873 -0.254005 -0.038433 0.194803 -0.269666 -0.031907 0.174783 -0.268033 -0.032302 0.201549 -0.278501 -0.020645 0.208564 --0.073666 0.038526 -0.352224 --0.076882 0.044216 -0.344481 --0.090345 0.049949 -0.34488 --0.079808 0.052262 -0.33116 --0.086114 0.059059 -0.313538 --0.101546 0.061002 -0.315492 --0.094518 0.067326 -0.284767 --0.106388 0.071042 -0.250493 --0.084369 0.069042 -0.247735 --0.073014 0.068797 -0.205774 --0.065091 0.019151 0.646697 --0.068711 0.018789 0.660834 --0.022373 0.108586 0.644445 --0.013195 0.113326 0.640083 -0.000977 0.116141 0.634141 -0.004099 0.080202 0.281578 -0.005222 0.084113 0.313587 --1.8e-05 0.091348 0.36624 -0.014112 0.093784 0.358834 -0.009048 0.088435 0.337116 -0.020807 0.0853 0.31137 -0.040382 0.086506 0.306746 -0.036479 0.078562 0.278794 -0.051479 0.066209 0.237014 -0.036242 0.07127 0.236575 -0.035281 0.068899 0.193525 -0.019448 0.074097 0.237143 -0.05561 -0.052972 -0.32207 -0.06282 0.040489 0.517542 -0.051013 0.054687 0.504998 -0.042858 0.041918 0.516592 -0.065208 0.06843 0.494175 -0.037299 0.066972 0.494908 -0.02119 0.059746 0.501441 -0.02674 0.044809 0.514452 -0.038298 0.035426 0.525313 -0.071308 0.027007 0.533041 -0.077062 0.031587 0.526262 -0.086859 0.03658 0.517987 -0.11124 0.022703 0.520396 -0.101501 0.044457 0.508542 -0.115815 0.045589 0.502309 -0.091657 0.06211 0.497272 -0.080638 0.080966 0.485791 -0.07637 0.070151 0.293696 -0.087457 0.048722 0.283502 -0.077093 0.059473 0.2667 -0.099334 0.030242 0.29242 -0.092998 0.032041 0.26222 -0.090592 0.023644 0.230841 -0.082636 0.042793 0.245419 -0.067806 0.058189 0.233638 -0.064536 0.055795 0.198126 -0.04641 0.092096 0.322679 -0.061535 0.083149 0.305984 -0.079014 0.078099 0.313706 -0.07703 0.084647 0.334748 -0.091849 0.065857 0.328967 -0.108059 0.051446 0.346879 -0.101082 0.039429 0.317875 -0.110719 0.01355 0.310582 -0.047532 -0.049776 -0.286305 -0.046867 -0.038187 -0.317862 -0.040231 -0.021887 -0.315602 --0.039663 0.064887 0.794549 --0.04651 0.07132 0.785701 --0.038541 0.084186 0.78246 --0.032592 0.059025 0.801776 --0.024175 0.069175 0.800655 --0.007675 0.0712 0.803918 --0.015757 0.080755 0.797041 --0.00745 0.092215 0.790085 --0.023277 0.089332 0.787695 --0.029903 0.097925 0.774864 --0.13845 3.5e-05 -0.507078 --0.133365 0.003981 -0.52854 --0.118126 0.005381 -0.527499 --0.127763 0.005296 -0.553194 --0.121695 0.003929 -0.580238 --0.13855 0.0021 -0.580595 --0.148539 -0.004026 -0.614203 --0.141288 -0.008128 -0.65282 --0.156359 -0.013014 -0.650997 -0.066578 -0.099193 -0.090488 --0.020935 0.110536 0.762662 --0.074793 -0.027179 0.725758 --0.078767 -0.010184 0.725264 --0.080834 0.008385 0.724145 --0.078249 -0.000118 0.707505 --0.076155 0.000473 0.689176 --0.067835 -0.042762 0.725758 --0.059417 -0.056343 0.725758 --0.000277 0.032619 0.537537 -0.005267 0.039781 0.519301 --0.051985 -0.063156 0.706182 --0.048867 -0.068495 0.725135 --0.037495 -0.077989 0.725432 -0.024103 0.101619 0.370269 -0.0319 0.096241 0.34902 -0.056128 0.09336 0.339387 --0.109701 0.006933 -0.501361 --0.102506 0.004261 -0.526311 --0.095928 0.003411 -0.551193 --0.088769 -0.000652 -0.579106 --0.105482 0.003165 -0.579427 --0.115801 0 -0.614731 --0.111182 -0.006827 -0.655254 --0.125404 -0.005933 -0.653705 --0.179507 -0.072641 -0.973405 --0.179736 -0.06291 -0.959185 --0.177626 -0.071836 -0.943035 --0.176403 -0.051299 -0.941831 --0.170692 -0.036558 -0.92544 --0.181173 -0.031149 -0.943277 --0.188787 -0.011566 -0.948821 --0.184485 0.00236 -0.937492 --0.197436 0.007013 -0.952195 -0.22727 0.014598 0.31173 -0.238361 0.013681 0.289874 -0.247408 0.005987 0.292578 -0.250942 0.011887 0.271593 -0.260531 0.00639 0.26041 -0.061291 0.014473 0.640484 -0.064319 0.036648 0.631863 -0.064492 0.023752 0.636896 -0.065379 0.01969 0.646737 -0.067876 0.0264 0.654196 -0.046216 -0.02182 0.600769 --0.048478 -0.033459 -0.350673 --0.028058 -0.038793 -0.20707 --0.022763 -0.03191 -0.164472 --0.030063 -0.052021 -0.167285 --0.03312 -0.064886 -0.128361 --0.078161 0.023262 0.691248 --0.080978 0.011701 0.692981 --0.076344 0.010431 0.681702 --0.064465 -0.038248 -0.423846 -0.025319 0.111368 0.385738 -0.303574 0.01443 0.063373 -0.310434 0.028986 0.052327 -0.307717 0.015772 0.090519 -0.314679 0.044944 0.019966 -0.310561 0.042088 0.055983 -0.303049 0.051605 0.07469 -0.306827 0.04219 0.097537 -0.299525 0.043386 0.128354 -0.305899 0.030051 0.126908 -0.304373 0.01851 0.147806 -0.306915 0.016735 0.123546 -0.302576 0.003439 0.11827 -0.295169 -0.009694 0.136612 -0.294152 -0.005943 0.112623 -0.294359 0.000894 0.081565 -0.323461 0.051886 -0.012258 -0.31762 0.048353 -0.001508 -0.314347 0.060279 0.004129 -0.049496 0.065008 0.164657 -0.031795 0.070223 0.160157 -0.017269 0.071324 0.154758 --0.146941 -0.076726 -0.464747 --0.158396 -0.12559 -0.614064 -0.015532 0.006863 -0.037583 -0.015801 0.017143 -0.036113 -0.006115 0.011659 -0.039677 -0.008433 -0.002873 -0.039343 --4.7e-05 0.003617 -0.040655 --0.006218 0.011679 -0.039691 --0.012729 0.034655 -0.030658 --0.12596 0.005521 -0.934599 --0.122568 -0.004845 -0.942795 --0.123125 -0.014672 -0.948338 --0.123788 -0.025068 -0.956534 --0.341135 0.114455 -0.093223 -0.283327 0.067105 0.019638 -0.293591 0.074868 0.000607 -0.303696 0.077585 0.000797 -0.301369 0.085086 -0.0127 -0.302858 0.089175 -0.033159 -0.304878 0.086785 -0.051677 --0.141076 -0.005643 -0.486465 --0.1314 0.000337 -0.482105 --0.127069 -0.001274 -0.460449 --0.123182 0.007158 -0.467277 --0.119739 0.015043 -0.450152 --0.114817 0.010324 -0.473312 --0.10375 0.010983 -0.473422 --0.012278 -0.088021 0.713298 --0.013227 -0.084451 0.698266 --0.026636 -0.078353 0.693522 --0.016851 -0.07652 0.685949 --0.006472 0.093335 0.584256 --0.008884 0.08669 0.576962 --0.018959 0.089208 0.584357 --0.026719 0.077269 0.582716 --0.035274 0.075271 0.589531 --0.043146 0.064831 0.596409 --0.043718 0.076945 0.600527 --0.051105 0.077523 0.612557 --0.046133 0.086744 0.60819 --0.043245 0.096606 0.616926 --0.037863 0.093977 0.607513 -0.075393 0.14152 0.374137 -0.086941 0.150895 0.389525 -0.104766 0.155557 0.405971 -0.055472 0.135163 0.378477 -0.099553 -0.102008 0.418153 -0.12026 -0.100115 0.445981 -0.124401 -0.087157 0.436601 -0.132895 -0.094732 0.454064 -0.14343 -0.091666 0.451211 -0.148486 -0.096747 0.473145 -0.167144 -0.097831 0.475341 -0.082814 -0.092688 0.359965 --0.134686 -0.019262 -0.431128 --0.130583 -0.011949 -0.44932 --0.137 -0.01838 -0.453731 --0.128557 -0.005102 -0.442871 --0.151621 -0.012736 -0.486601 --0.143161 -0.013686 -0.46922 --0.145265 -0.023601 -0.458198 --0.154182 -0.028671 -0.468872 --0.14918 -0.034021 -0.45351 --0.150857 -0.045031 -0.452753 --0.147695 -0.037346 -0.42967 --0.14553 -0.040588 -0.397805 --0.143761 -0.031171 -0.410357 --0.139706 -0.022557 -0.408004 --0.133982 -0.011764 -0.420962 --0.130189 -2.3e-05 -0.434238 --0.130731 -0.059971 -0.402214 --0.121147 -0.064038 -0.400681 --0.125873 -0.068746 -0.427178 --0.108189 -0.06546 -0.398883 --0.095316 -0.066502 -0.397735 --0.083395 -0.008128 -0.615211 --0.083465 -0.018747 -0.656748 --0.09549 -0.01114 -0.656054 --0.10805 -0.017318 -0.701964 --0.345052 0.050757 -0.075733 --0.340852 0.040872 -0.077866 --0.336268 0.045862 -0.051459 --0.332708 0.03217 -0.080015 --0.320226 0.028715 -0.057207 --0.315513 0.029836 -0.034959 --0.309902 0.030384 -0.019142 --0.323412 0.038281 -0.031359 --0.328846 0.05104 -0.026909 --0.322347 0.05044 -0.011265 --0.328773 0.070373 -0.02519 --0.324824 0.085885 -0.025156 --0.335409 0.090632 -0.048177 --0.340413 0.105961 -0.07299 --0.355039 0.079322 -0.09346 --0.354729 0.070641 -0.094084 --0.352009 0.069524 -0.083348 --0.351945 0.060515 -0.095032 --0.349183 0.058431 -0.083911 --0.349528 0.052311 -0.095002 --0.013355 0.039852 0.594693 --0.024763 0.044374 0.593548 --0.054537 0.031464 0.616147 --0.041883 0.035996 0.605149 --0.033948 0.049288 0.593243 --0.0564 0.065484 0.618657 --0.047072 0.09933 0.625654 --0.038556 0.102139 0.627143 -0.038416 -0.043774 -0.248688 --0.148015 -0.107669 -0.979672 --0.133355 -0.105551 -0.979672 --0.125761 -0.088709 -0.978708 --0.158094 -0.022202 -0.486381 --0.172153 -0.033047 -0.260095 --0.164795 -0.038678 -0.296103 --0.154686 -0.042198 -0.328648 --0.146163 -0.046645 -0.362297 --0.175247 -0.088746 0.512573 --0.115347 0.043952 -0.360718 --0.123524 0.03786 -0.375678 --0.117734 0.034503 -0.397764 --0.126675 0.028729 -0.39779 --0.127998 0.019027 -0.419943 --0.128992 0.067014 -0.016683 --0.145541 0.056575 -0.015304 --0.154535 0.052006 -0.049873 --0.156895 0.041146 -0.013534 -0.030724 -0.025361 -0.247007 -0.361949 0.095031 -0.136579 -0.017074 -0.003681 -0.036584 -0.01259 -0.019473 -0.035051 -0.011123 -0.036474 -0.029308 -0.348279 0.123165 -0.119123 -0.351082 0.119023 -0.111683 -0.355221 0.109178 -0.109376 -0.355448 0.123344 -0.125317 -0.354377 0.127636 -0.135881 --0.090973 0.143064 0.374274 --0.075512 0.14156 0.374109 --0.050079 0.136278 0.403542 --0.051536 0.13747 0.389704 --0.034916 0.125166 0.390231 --0.055592 0.135202 0.378449 --0.062288 0.129571 0.366088 --0.071 0.123602 0.358853 --0.083811 0.118598 0.35427 --0.088873 0.131855 0.363284 --0.100924 0.141979 0.37695 --0.109577 0.13932 0.377592 -0.082637 0.018265 0.705134 --0.039964 0.056346 0.594023 --0.007824 0.077675 0.574045 --0.052266 -0.100272 -0.03746 --0.041446 -0.087651 -0.060252 --0.061914 -0.113622 -0.024715 --0.073924 -0.108272 -0.035007 --0.097503 -0.11114 -0.028186 --0.081836 -0.105332 -0.058315 --0.092393 -0.103408 -0.091947 --0.068174 -0.099288 -0.091332 --0.049442 -0.089607 -0.090816 --0.036659 -0.075099 -0.089509 --0.065044 -0.123263 -0.010253 --0.009836 0.115743 0.648737 -0.327237 0.060264 -0.098716 -0.317402 0.056792 -0.083903 -0.323016 0.066262 -0.090659 -0.325027 0.078099 -0.08949 -0.328238 0.072616 -0.096421 -0.336604 0.073106 -0.106272 -0.341772 0.073187 -0.117031 -0.345532 0.067716 -0.130501 -0.344925 0.062626 -0.120462 -0.341867 0.06099 -0.11061 -0.364758 0.09914 -0.149249 --0.109966 0.110749 0.356492 --0.109311 0.125573 0.364086 --0.095597 0.114976 0.35443 --0.118885 0.132924 0.374594 --0.12195 0.107737 0.360565 --0.134508 0.103165 0.368117 --0.127253 0.121475 0.370784 --0.125349 0.136668 0.384532 --0.056701 0.132894 0.430754 --0.05099 0.134502 0.41673 -0.039707 -0.057384 0.588539 -0.044777 -0.051595 0.583812 -0.046226 -0.037371 0.588 -0.045744 -0.025078 0.584625 --0.353014 0.125885 -0.134685 --0.171172 -0.082649 -0.978226 --0.160534 -0.093928 -0.979672 --0.174519 -0.08255 -0.925923 --0.108082 -0.108063 -0.798143 --0.11853 -0.1142 -0.831174 --0.124871 -0.122406 -0.796678 --0.149434 -0.05543 -0.451883 -0.104513 -0.119856 -0.006538 -0.0978 -0.111172 -0.028164 -0.073252 -0.108298 -0.034989 -0.080957 -0.10535 -0.058061 -0.131654 -0.0198 0.347079 -0.125752 0.000379 0.343833 -0.135014 0.009127 0.377787 -0.126958 0.034315 0.373538 -0.142655 0.022913 0.401574 -0.148499 0.040696 0.417274 -0.149856 0.024669 0.463786 -0.145439 0.003689 0.46021 -0.124828 -0.071875 0.412865 -0.122421 -0.065139 0.370851 -0.111641 -0.069488 0.340433 -0.117589 -0.047997 0.327629 -0.111689 -0.028426 0.285855 -0.120236 -0.024706 0.314792 -0.121132 -0.008466 0.319208 --0.172876 -0.001797 -0.921102 --0.166697 -0.021132 -0.914594 --0.161038 -0.003711 -0.909773 --0.140046 -0.007952 -0.908327 --0.150225 -0.003921 -0.904471 --0.149673 0.015229 -0.910738 --0.136725 0.029957 -0.921102 -0.020953 0.113196 0.423049 -0.019571 0.112543 0.404996 -0.029089 0.111175 0.441785 -0.03473 0.124593 0.422144 -0.050871 0.134464 0.416757 -0.049959 0.136238 0.403569 -0.067984 0.14531 0.39275 -0.051416 0.137432 0.389731 -0.034804 0.125134 0.390255 -0.039183 0.107472 0.457142 -0.015921 -0.058296 -0.02911 -0.020451 -0.081964 -0.028563 --0.338809 0.059569 -0.107587 --0.343537 0.061005 -0.11919 --0.340383 0.071566 -0.115761 --0.130676 0.136435 0.392085 --0.139835 0.118599 0.388783 --0.135497 0.131855 0.40028 --0.140317 0.11908 0.41144 --0.131158 0.137399 0.411849 --0.127783 0.139086 0.421009 -0.075004 -0.077493 0.30713 -0.093165 -0.079862 0.333638 -0.102578 -0.060463 0.304957 -0.078029 0.023319 0.691211 --0.057586 -0.034515 -0.389729 --0.132363 0.124624 0.43241 --0.123592 0.137482 0.430947 --0.118679 0.131781 0.442643 -0.002832 0.090679 0.579852 --0.141763 0.100521 0.378563 --0.355368 0.062627 -0.114814 --0.349396 0.078605 -0.105548 --0.288757 0.056509 -0.018076 --0.286935 0.056323 -0.006271 --0.288629 0.044749 -0.008982 --0.287855 0.065227 -0.000294 --0.280113 0.053221 0.012952 --0.267767 0.047503 0.045014 --0.269139 0.037245 0.041218 --0.254836 0.020732 0.073284 --0.271923 0.02598 0.039028 --0.284591 0.030382 0.008051 --0.295296 0.031881 -0.01158 --0.30377 0.031437 -0.037083 --0.297344 0.041176 -0.043246 --0.292216 0.053984 -0.034375 --0.294587 0.072448 -0.033277 --0.301496 0.08743 -0.031962 --0.300253 0.083639 -0.011707 --0.302827 0.076435 0.001586 --0.292969 0.074014 0.001193 --0.281513 0.06279 0.019897 -0.086165 0.000232 0.696923 -0.008936 0.071248 0.803965 -0.000872 0.107344 0.613001 --0.351867 0.045028 -0.160324 --0.184334 -0.090987 0.478655 --0.180866 -0.093797 0.455579 --0.192861 -0.084703 0.435922 --0.175791 -0.091508 0.432581 --0.17016 -0.086396 0.41059 --0.101383 -0.071651 -0.425071 --0.117583 -0.078589 -0.445556 --0.129201 -0.076726 -0.446516 --0.062555 -0.045563 -0.390664 --0.070265 -0.057484 -0.394165 --0.081778 -0.063749 -0.395622 --0.018647 0.109092 0.657665 --0.01261 0.109655 0.669207 -0.0612 -0.113649 -0.024697 -0.064575 -0.12329 -0.010235 -0.047695 -0.114606 -0.018917 -0.030815 -0.113587 -0.011691 -0.030815 -0.100812 -0.024706 -0.045019 -0.011214 0.573057 --0.137942 -0.072298 -0.448699 --0.144563 -0.066044 -0.450807 --0.025506 0.112792 0.696714 --0.017276 0.122161 0.725792 --0.021595 0.119803 0.709461 --0.142078 -0.058424 -0.430818 --0.056606 -0.128518 0.003003 --0.08336 -0.12659 0.001076 --0.097366 -0.127499 0.015291 --0.103809 -0.119817 -0.006566 -0.039376 0.10617 0.663733 -0.028632 0.106834 0.66425 -0.033957 0.107383 0.64946 -0.016359 0.043991 0.515559 -0.044878 0.100847 0.681489 -0.045223 0.103968 0.670256 -0.052914 0.104231 0.65826 -0.044621 0.10681 0.652899 -0.076213 0.010488 0.681667 -0.071646 0.023293 0.67267 -0.362776 0.097661 -0.123083 -0.359574 0.088524 -0.112757 -0.312943 0.044951 -0.07454 -0.308352 0.056671 -0.069257 -0.306424 0.073658 -0.061704 -0.314519 0.093868 -0.068159 -0.324215 0.107993 -0.078378 -0.321862 0.097355 -0.08002 -0.329791 0.096935 -0.09254 -0.327202 0.088566 -0.090317 -0.321923 0.034752 -0.080185 -0.024801 0.001349 -0.202991 -0.031492 0.011056 -0.244461 -0.028399 -0.006603 -0.245536 -0.033205 -0.015061 -0.282364 --0.028547 0.103573 0.625856 --0.032026 0.105467 0.63514 --0.081198 0.054688 0.090468 -0.058176 0.097731 0.647868 -0.060032 0.093784 0.669735 -0.063961 0.082176 0.666919 -0.06654 0.070166 0.676537 -0.065813 0.070004 0.659307 -0.066309 0.056881 0.649331 -0.062987 0.07486 0.643841 -0.059015 0.089006 0.6384 --0.03838 0.035432 0.525308 --0.071382 0.027007 0.533041 --0.009674 0.106762 0.611441 --0.019029 0.103085 0.608624 -0.039222 0.019263 -0.27972 -0.362573 0.106105 -0.142558 -0.357631 0.10375 -0.131248 -0.353224 0.101317 -0.116185 -0.296483 0.061471 0.059544 -0.291315 0.057051 0.098559 -0.276438 0.058275 0.126668 -0.289726 0.052774 0.12868 -0.290853 0.046799 0.150876 -0.310791 0.072738 0.004206 -0.303663 0.06729 0.025 -0.284757 0.064199 0.058181 --0.139835 0.103654 0.435928 --0.132604 0.110644 0.44557 --0.130916 0.094977 0.458971 --0.124074 0.116031 0.45141 --0.111929 0.11973 0.45508 --0.100119 0.120694 0.456286 --0.086715 0.123262 0.455026 --0.360632 0.093308 -0.135502 --0.349583 0.084734 -0.116476 --0.358255 0.086801 -0.111672 --0.131878 0.000476 -0.92062 --0.12911 0.019234 -0.927128 -0.198889 -0.026778 0.227738 -0.192185 -0.023207 0.243713 -0.188352 -0.031844 0.264659 -0.185173 -0.020391 0.264729 -0.176599 -0.017861 0.287774 --0.013024 -0.088241 0.72662 --0.0247 -0.08469 0.720404 --0.033508 -0.078855 0.707401 --0.0416 -0.069884 0.69463 --0.027312 0.044822 0.514443 -0.018951 0.105409 0.618947 -0.057516 0.093339 0.67944 -0.278125 0.048336 0.17281 -0.263154 0.054575 0.169187 -0.262592 0.058539 0.14524 -0.247085 0.054634 0.163441 -0.233057 0.048502 0.156696 -0.282102 0.010812 0.218659 -0.288074 0.024504 0.19717 -0.297528 0.02164 0.172297 -0.290382 0.036594 0.173918 -0.065553 0.045494 0.639548 -0.068224 0.03931 0.65204 -0.069891 0.033191 0.660931 --0.083804 0.074907 -0.165948 --0.062697 0.064238 -0.16485 --0.045673 0.048194 -0.163564 --0.03268 0.028352 -0.161928 --0.127843 0.065599 -0.253296 --0.118913 0.072745 -0.210048 --0.130514 0.070531 -0.168479 --0.107684 0.076855 -0.167466 --0.096156 0.078963 -0.126465 --0.083653 -0.133012 0.031918 --0.10907 -0.123137 0.031923 --0.118079 -0.11643 0.049941 --0.159349 0.041309 -0.255529 --0.155791 0.0504 -0.21274 --0.146525 0.05484 -0.254917 --0.162423 0.043235 -0.170205 --0.148711 0.058947 -0.169549 --0.1406 0.067376 -0.127538 --0.133779 0.058198 -0.289618 --0.11935 0.057318 -0.31754 --0.168086 0.02405 -0.256279 --0.173604 0.00463 -0.257509 --0.17464 0.014528 -0.214836 --0.179462 0.002306 -0.17347 --0.172535 0.024622 -0.171551 --0.168602 0.03471 -0.128899 --0.06391 0.131373 0.441954 -0.095027 -0.048825 0.262066 -0.103824 -0.036545 0.269255 -0.104098 -0.015764 0.243629 --0.0991 0.017944 -0.450663 -0.262731 0.05789 0.122908 -0.265882 0.059393 0.093437 -0.249381 0.05205 0.116538 -0.241193 0.043654 0.110039 -0.235215 0.046569 0.132123 -0.222589 0.036845 0.14968 -0.146018 0.020217 -0.352553 -0.143756 0.007836 -0.390439 -0.14439 0.017838 -0.368618 -0.139017 0.027615 -0.35283 -0.12709 0.038621 -0.361158 -0.131022 0.036125 -0.351555 -0.355028 0.04387 -0.128252 -0.353348 0.049278 -0.116616 -0.349146 0.056385 -0.106308 -0.35089 0.054056 -0.096199 -0.350544 0.060176 -0.085108 -0.346414 0.052502 -0.07693 -0.337629 0.047607 -0.052655 -0.342214 0.042617 -0.079063 -0.33407 0.033915 -0.081211 -0.341084 0.037101 -0.099069 -0.344392 0.039642 -0.121249 --0.141336 -0.051267 -0.399824 -0.298792 0.005093 0.16812 -0.290602 -0.00859 0.188816 -0.293927 -0.009816 0.162202 -0.283459 -0.021475 0.155186 -0.27585 -0.003524 0.237141 -0.283366 -0.005735 0.214482 --0.351907 0.099595 -0.115108 --0.34249 0.092899 -0.112093 --0.345768 0.096557 -0.122526 --0.349666 0.095542 -0.136017 --0.356314 0.102028 -0.130171 --0.312351 0.036325 -0.004647 --0.305342 0.029027 -0.008073 -0.191614 0.011738 0.323365 -0.203097 0.014525 0.328262 -0.202498 0.011696 0.352268 -0.214997 0.013491 0.332878 -0.226236 0.008727 0.336797 -0.351124 0.080663 -0.106541 -0.356378 0.081055 -0.094596 -0.353367 0.071267 -0.084535 -0.356095 0.072365 -0.095294 -0.35739 0.073907 -0.111625 -0.337536 0.088233 -0.102831 -0.343807 0.094623 -0.113171 --0.02455 0.00862 -0.161409 --0.02085 -0.011834 -0.162222 --0.012323 0.119858 0.745726 -0.154903 -0.108342 -0.84821 -0.157955 -0.103488 -0.89052 -0.162337 -0.097395 -0.84577 -0.160152 -0.106884 -0.925923 -0.163093 -0.093571 -0.914112 -0.169384 -0.0825 -0.925923 -0.163187 -0.078345 -0.907845 -0.158667 -0.064508 -0.89748 -0.162831 -0.075833 -0.88042 -0.167599 -0.073578 -0.841037 -0.167119 -0.084966 -0.841937 -0.172359 -0.091098 -0.788958 -0.179314 -0.084468 -0.735822 -0.174054 -0.098491 -0.737941 -0.172653 -0.108092 -0.692848 -0.166863 -0.110119 -0.739513 -0.156392 -0.119247 -0.743145 -0.157734 -0.113673 -0.792971 -0.146709 -0.116839 -0.829945 -0.139238 -0.123252 -0.795671 -0.135221 -0.119519 -0.851526 -0.126098 -0.118567 -0.85216 -0.128453 -0.117836 -0.90182 -0.120161 -0.116392 -0.93725 -0.129375 -0.12077 -0.938938 -0.129172 -0.124665 -0.957739 -0.140594 -0.122729 -0.948098 -0.14944 -0.122801 -0.960149 -0.154592 -0.115999 -0.943517 -0.166877 -0.105317 -0.947133 -0.065331 0.074522 0.689774 -0.069246 0.061208 0.691007 -0.071205 0.059059 0.707851 -0.35675 0.064282 -0.116065 -0.353311 0.062239 -0.096241 -0.355051 0.062522 -0.127898 -0.361289 0.071473 -0.127738 -0.35018 0.077184 -0.123737 -0.20152 -0.056893 0.293143 -0.203629 -0.049299 0.268706 -0.194583 -0.041604 0.266002 -0.204535 -0.04329 0.248854 -0.205009 -0.037103 0.231537 -0.140183 -0.036419 -0.8667 -0.134024 -0.037348 -0.836944 -0.143458 -0.038358 -0.836309 -0.122109 -0.041502 -0.8244 -0.125832 -0.031999 -0.795256 -0.117287 -0.025912 -0.751044 -0.128883 -0.023115 -0.748801 -0.134097 -0.01391 -0.699996 -0.139656 -0.024143 -0.746943 -0.152452 -0.029692 -0.74438 -0.146039 -0.034038 -0.792764 -0.153367 -0.04399 -0.821778 -0.163786 -0.047167 -0.789462 -0.160572 -0.053136 -0.836354 -0.165379 -0.062623 -0.837463 -0.159322 -0.056693 -0.872748 -0.135524 -0.036067 -0.889768 -0.128481 -0.032994 -0.905194 -0.127864 -0.044391 -0.893624 -0.12024 -0.059215 -0.895553 -0.120564 -0.052687 -0.872655 -0.109133 -0.059543 -0.840626 -0.116183 -0.050045 -0.839212 -0.109217 -0.043154 -0.796386 --0.019678 0.02905 -0.029532 --0.015918 0.01718 -0.036137 --0.027782 0.104204 0.685235 --0.018875 0.106096 0.678748 --0.044037 0.103878 0.670194 --0.038982 0.102331 0.681333 --0.128709 0.010087 -0.432263 --0.12282 0.016837 -0.434716 --0.115201 0.022097 -0.434582 --0.111437 0.030062 -0.419783 --0.105629 0.024339 -0.434984 --0.093734 0.023146 -0.434976 --0.181137 -0.019254 -0.175074 --0.162184 0.043679 -0.087926 --0.175245 -0.015389 -0.25902 --0.177955 -0.02705 -0.217951 --0.177569 -0.03962 -0.176278 -0.035154 -0.122722 0.002771 --0.052378 0.104259 0.65844 --0.043756 0.106801 0.652945 --0.03289 0.107281 0.649359 -0.093246 0.049027 0.102835 -0.102315 0.040455 0.113433 -0.081108 0.054715 0.090481 -0.083986 0.055223 0.114438 -0.074667 0.060907 0.130241 -0.086741 0.049708 0.133324 -0.090962 0.038731 0.152601 -0.097319 0.040624 0.13197 -0.110442 0.029584 0.124656 -0.11543 0.014141 0.134661 --0.146583 0.097629 0.39423 --0.147066 0.097629 0.408934 --0.144415 0.099798 0.424119 --0.075479 0.127276 0.449668 --0.137907 0.071356 0.459767 --0.145378 0.063402 0.446028 -0.002666 0.099924 0.594166 --0.208136 -0.06588 0.463823 --0.213936 -0.055034 0.444634 --0.206495 -0.070744 0.440044 --0.214464 -0.036885 0.448758 --0.221066 -0.043897 0.422246 --0.2282 -0.032688 0.395547 --0.227826 -0.044995 0.392802 --0.236249 -0.044725 0.363377 --0.221761 -0.057822 0.388709 --0.212382 -0.068579 0.384699 --0.20756 -0.072435 0.413562 --0.187 -0.085581 0.498671 --0.197969 -0.077029 0.482777 --0.206481 -0.058685 0.487615 --0.203208 -0.050881 0.507905 --0.20808 -0.037902 0.491988 --0.201669 -0.018703 0.494444 --0.210263 -0.026948 0.47232 --0.208053 -0.017687 0.451214 -0.16996 -0.027323 0.310191 -0.170357 -0.015106 0.311774 -0.166048 -0.012376 0.331959 -0.17441 -0.003851 0.314699 -0.181753 0.005421 0.318704 --0.1098 0.054429 -0.334104 --0.14562 0.080033 0.435592 -0.060488 -0.068938 0.271743 -0.078781 -0.059829 0.264962 -0.092697 -0.040887 0.223772 -0.093348 -0.035422 0.193415 -0.098751 -0.019246 0.193125 -0.100697 0.002736 0.176856 -0.101353 -0.006403 0.205791 -0.095902 0.006341 0.221401 -0.038794 -0.006233 -0.313056 -0.041719 0.010433 -0.311486 -0.066881 -0.020749 -0.416186 -0.029509 -0.087462 0.126235 -0.022217 -0.076703 0.144776 -0.032796 -0.071917 0.162044 -0.011425 -0.071799 0.162332 -0.002712 -0.068146 0.176001 -0.070781 0.112051 0.464061 -0.054872 0.10699 0.465579 -0.072708 0.096866 0.474979 -0.050534 0.119282 0.451526 -0.093919 0.092528 0.477149 -0.114648 0.086984 0.475703 -0.105395 0.105146 0.46749 -0.111811 0.119691 0.455108 -0.1 0.120655 0.456313 -0.097831 0.134636 0.443876 -0.086595 0.123223 0.455053 -0.07536 0.127236 0.449695 -0.080663 0.140493 0.434606 -0.06379 0.131334 0.441982 -0.056581 0.132855 0.430781 -0.179852 0.005927 0.340541 -0.048671 0.026118 -0.310259 -0.059727 0.041338 -0.310499 -0.027022 0.037246 0.523155 --0.080366 0.042976 -0.359844 --0.072915 0.03363 -0.369455 --0.070227 0.013891 -0.415307 --0.069241 0.022839 -0.390911 --0.076875 0.030483 -0.393719 --0.085119 0.027745 -0.418972 --0.086507 0.036825 -0.396545 --0.096641 0.042094 -0.387468 --0.087837 0.044531 -0.374711 --0.092277 0.048103 -0.362594 --0.103086 0.047746 -0.360488 --0.104066 0.049317 -0.349409 -0.275432 0.062709 0.0551 -0.269288 0.055547 0.049494 -0.25155 0.043835 0.081332 -0.312765 0.085286 -0.010822 --0.006119 0.113561 0.764306 -0.000738 0.103834 0.778899 -0.056372 -0.128537 0.003258 -0.044313 -0.130652 0.018679 -0.05757 -0.134243 0.033141 -0.083339 -0.126623 0.001097 --0.155492 -0.028329 0.356208 --0.160749 -0.032802 0.345004 --0.161044 -0.021111 0.346178 --0.166167 -0.012336 0.331933 --0.164113 -0.010625 0.349268 --0.170574 -0.001596 0.353845 --0.16219 -0.006465 0.362443 --0.164373 -0.002133 0.375173 --0.154414 -0.01074 0.370753 --0.163482 -0.053438 0.350923 --0.160466 -0.043558 0.34741 --0.165475 -0.036245 0.329176 --0.18622 -0.077512 0.51589 --0.193917 -0.061157 0.520285 --0.195394 -0.04444 0.523948 --0.176073 0.003874 0.375161 --0.167484 0.004652 0.390015 --0.172233 0.009446 0.407131 -0.075831 -0.027037 0.725898 -0.068873 -0.04262 0.725898 -0.061801 -0.048767 0.745837 --0.27555 0.062748 0.055073 --0.269406 0.055586 0.049467 -0.103573 0.004244 -0.526283 -0.096997 0.003408 -0.551167 -0.088488 0.000855 -0.525091 -0.106551 0.00317 -0.5794 -0.089838 -0.000647 -0.579079 -0.084256 -0.008196 -0.615646 -0.075129 -0.008559 -0.578207 -0.064432 -0.020335 -0.577323 -0.068614 -0.011633 -0.548927 -0.065155 -0.016647 -0.522231 -0.074816 -0.006275 -0.523723 -0.081531 -0.003771 -0.502802 -0.078044 -0.009858 -0.481754 -0.119195 0.005364 -0.527473 -0.134434 0.003972 -0.528513 -0.128831 0.005293 -0.553166 -0.139619 0.002105 -0.580567 -0.122764 0.003934 -0.580212 -0.116662 -6.7e-05 -0.615168 -0.349613 0.067725 -0.140039 -0.356089 0.091025 -0.092513 -0.353287 0.086051 -0.082628 -0.068413 0.142812 0.41341 -0.206081 -0.033746 0.219483 -0.078094 -0.064043 0.179812 -0.082596 -0.049383 0.190776 -0.067026 -0.061547 0.206463 -0.097546 -0.038496 0.172632 -0.043016 -0.069454 0.25707 -0.056181 -0.066368 0.240689 -0.051476 -0.065838 0.21055 -0.035925 -0.066514 0.205479 -0.04313 -0.069236 0.184395 -0.050298 -0.074615 0.161752 -0.055473 -0.086776 0.145536 -0.066807 -0.080225 0.156563 -0.080701 -0.090169 0.145461 -0.078786 -0.075462 0.161242 -0.090862 -0.065936 0.159389 -0.106573 -0.068518 0.141109 -0.102218 -0.050143 0.15512 -0.111741 -0.029199 0.149358 -0.112008 -0.070891 -0.906881 -0.11057 -0.07076 -0.880396 -0.102978 -0.080562 -0.84563 -0.103886 -0.06994 -0.844271 -0.096618 -0.061591 -0.797183 -0.0882 -0.060651 -0.752721 -0.091109 -0.04912 -0.75201 -0.08615 -0.033149 -0.705572 -0.09666 -0.040182 -0.752628 -0.106557 -0.032314 -0.752003 -0.14956 -0.001313 -0.529318 -0.144855 -0.124841 -0.745393 -0.149778 -0.127288 -0.700318 -0.177309 -0.101695 -0.647878 -0.167115 -0.118095 -0.650531 -0.159465 -0.125585 -0.614036 -0.154711 -0.128161 -0.654369 -0.139654 -0.133976 -0.656646 -0.128731 -0.138171 -0.616951 -0.126036 -0.135417 -0.659802 -0.111818 -0.132281 -0.660855 -0.124126 -0.130883 -0.706812 -0.122834 -0.126243 -0.750746 -0.132821 -0.126997 -0.748729 -0.058829 -0.030454 -0.520769 -0.06317 -0.022602 -0.499983 -0.064227 -0.028785 -0.47925 -0.069343 -0.018317 -0.480426 -0.076388 -0.016697 -0.464359 -0.077672 -0.020169 -0.448528 -0.353537 0.0987 -0.091601 -0.346735 0.096378 -0.073406 -0.341774 0.107706 -0.074186 -0.336771 0.092377 -0.049375 -0.326186 0.08763 -0.026353 -0.330134 0.072118 -0.026387 -0.330207 0.052784 -0.028106 -0.324773 0.040026 -0.032555 -0.124965 -0.02669 0.131936 -0.116345 -0.007176 0.142504 -0.106752 0.009716 0.15506 -0.095406 0.023264 0.165686 -0.09808 -0.130509 -0.616704 -0.099527 -0.124822 -0.662285 -0.087129 -0.110283 -0.661788 -0.102193 -0.117634 -0.710458 -0.104738 -0.111843 -0.754197 -0.113192 -0.120451 -0.752962 -0.120129 -0.121547 -0.798324 -0.116907 -0.114396 -0.832544 -0.108758 -0.085321 0.124834 -0.121176 -0.06699 0.122271 -0.13404 -0.064346 0.105192 -0.130046 -0.046107 0.118861 -0.339622 0.118009 -0.112037 -0.334976 0.116204 -0.088929 -0.342497 0.1162 -0.094419 -0.073351 0.008257 0.671288 -0.10554 -0.092843 -0.849852 -0.095638 -0.08453 -0.799001 -0.090968 -0.087454 -0.754633 -0.088128 -0.07297 -0.753181 -0.078976 -0.058512 -0.707107 --0.075424 0.009123 -0.435344 -0.316876 0.03158 -0.036155 -0.086249 0.027853 0.199251 -0.08641 0.035636 0.172472 -0.075024 0.044607 0.178318 -0.063516 0.056965 0.168818 --0.106758 -0.031887 -0.749253 --0.096861 -0.039755 -0.749878 --0.110329 -0.043009 -0.794095 --0.118654 -0.050742 -0.838319 --0.111159 -0.06052 -0.840215 -0.162173 -0.024273 -0.695147 -0.163692 -0.037602 -0.74226 -0.172937 -0.048276 -0.73928 -0.180361 -0.047124 -0.689526 -0.178379 -0.05903 -0.736715 -0.180512 -0.072165 -0.735769 -0.174036 -0.067408 -0.788005 --0.01866 0.041565 -0.022306 -0.160106 -0.005357 -0.554471 --0.33064 0.11508 -0.074983 --0.311601 0.083779 -0.009789 --0.314357 0.096724 -0.02922 --0.32229 0.110319 -0.053009 -0.184241 -0.076208 -0.687737 --0.083623 0.018781 -0.434761 -0.183078 -0.041209 0.287631 -0.077348 -0.10364 0.128523 -0.094965 -0.097446 0.127334 -0.10717 -0.101699 0.110056 -0.05856 0.088006 0.689861 -0.051062 0.096194 0.687617 -0.348306 0.101164 -0.105268 -0.347888 0.106007 -0.114837 -0.355884 0.113699 -0.120724 -0.009858 0.094499 0.586661 -0.108833 -0.085105 -0.914594 -0.10845 -0.099236 -0.923753 -0.109957 -0.097255 -0.892333 -0.111542 -0.104529 -0.851505 -0.097953 -0.099412 -0.47666 -0.111357 -0.101905 -0.477447 -0.119589 -0.112055 -0.499903 -0.059946 0.08543 0.699312 --0.170476 -0.015066 0.311747 --0.174529 -0.003813 0.314673 --0.185116 0.005111 0.293771 --0.181873 0.00546 0.318677 --0.191732 0.011776 0.323338 --0.179971 0.005966 0.340514 --0.179228 0.004671 0.361014 --0.19039 0.007652 0.367844 -0.072639 -0.099523 -0.614258 -0.0775 -0.093825 -0.66106 -0.071185 -0.075868 -0.660395 -0.084233 -0.088981 -0.709949 -0.096406 -0.099648 -0.754415 -0.107078 -0.108411 -0.800435 -0.26745 -0.00378 0.255424 -0.267637 -0.016481 0.253101 -0.260933 -0.028107 0.26999 -0.261114 -0.004045 0.273784 -0.25364 -0.003772 0.29392 -0.256818 -0.015226 0.29385 -0.252192 -0.024433 0.317098 -0.256449 -0.026587 0.292379 -0.252545 -0.037425 0.289561 --0.148709 -0.127292 -0.700346 --0.138585 -0.133981 -0.656673 --0.153642 -0.128166 -0.654397 --0.125176 -0.13535 -0.659367 --0.166046 -0.1181 -0.650557 --0.176239 -0.101699 -0.647905 --0.171584 -0.108097 -0.692875 --0.172985 -0.098496 -0.737968 --0.165794 -0.110124 -0.73954 --0.155324 -0.119252 -0.743172 --0.143786 -0.124846 -0.74542 --0.131961 -0.12693 -0.748294 --0.12239 -0.126032 -0.749386 --0.123473 -0.130744 -0.705913 --0.111165 -0.132142 -0.659956 -0.160924 -0.021151 0.346205 -0.163994 -0.010665 0.349294 -0.16207 -0.006504 0.36247 -0.170454 -0.001635 0.353872 -0.179108 0.004632 0.361041 -0.175953 0.003835 0.375187 -0.19027 0.007613 0.367871 -0.198506 0.009604 0.379501 -0.060439 -0.06298 -0.613617 -0.068768 -0.059883 -0.659006 -0.070615 -0.043766 -0.658497 -0.353396 0.053089 -0.127413 -0.241181 -0.04512 0.336466 -0.245234 -0.033866 0.339391 -0.23613 -0.044764 0.363403 -0.245631 -0.02165 0.340974 -0.242331 -0.009756 0.341047 -0.2369 -0.020241 0.36773 -0.225764 -0.019446 0.394917 -0.228079 -0.032727 0.395573 -0.220946 -0.043937 0.422273 -0.227706 -0.045034 0.392829 -0.221642 -0.057862 0.388736 -0.207441 -0.072475 0.413589 -0.212263 -0.068618 0.384727 -0.201974 -0.072895 0.375283 -0.219409 -0.064794 0.355389 -0.223978 -0.060709 0.3278 -0.233838 -0.054393 0.332461 -0.244235 -0.046525 0.311355 --0.246344 -0.046865 0.248314 --0.246726 -0.046279 0.264919 --0.23634 -0.052093 0.281321 --0.245836 -0.04601 0.28581 --0.244354 -0.046486 0.311327 --0.252664 -0.037387 0.289534 --0.256568 -0.026548 0.292352 --0.235924 -0.050107 0.243671 --0.224655 -0.049757 0.239529 --0.225627 -0.052112 0.255722 --0.2142 -0.053688 0.272322 --0.22566 -0.054685 0.276767 --0.224999 -0.058884 0.302177 -0.172787 -0.090507 -0.947133 -0.172491 -0.071786 -0.943035 -0.174601 -0.062859 -0.959185 -0.171268 -0.051249 -0.941831 -0.176039 -0.031099 -0.943277 -0.183272 -0.024297 -0.958462 --0.182568 -0.083839 -0.644914 --0.183173 -0.076213 -0.687764 --0.179451 -0.07217 -0.735796 --0.178245 -0.084473 -0.73585 --0.172324 -0.091111 -0.788981 --0.165041 -0.06852 0.372711 --0.165792 -0.076618 0.390739 --0.182487 -0.078599 0.387542 --0.202092 -0.072856 0.375256 --0.192214 -0.07212 0.365583 --0.198164 -0.066806 0.340792 --0.181422 -0.0669 0.357366 --0.169688 -0.061476 0.354624 --0.175191 -0.05662 0.334131 --0.190446 0.003531 0.269551 --0.197274 0.012155 0.273275 --0.20633 0.017395 0.277484 --0.216569 0.019145 0.281759 --0.204031 0.016668 0.302641 --0.203217 0.014564 0.328234 --0.170079 -0.027283 0.310164 --0.176718 -0.017823 0.287748 --0.185292 -0.020352 0.264702 --0.186102 -0.008149 0.266454 -0.363363 0.07473 -0.150603 --0.107552 0.038514 -0.397668 --0.177325 -0.059035 -0.736742 --0.173896 -0.068383 -0.788751 -0.360636 0.075305 -0.160142 --0.189058 0.010659 0.403907 --0.198626 0.009643 0.379475 --0.202387 0.007865 0.396803 --0.212762 0.002657 0.389941 --0.206169 -0.001395 0.41268 --0.213588 -0.012012 0.413809 --0.205328 -0.008308 0.434608 --0.195397 0.000483 0.451738 --0.18388 0.006758 0.473118 --0.187941 -0.003168 0.494719 -0.062872 0.064146 0.147546 -0.159288 0.119897 -0.961836 -0.119568 0.115217 -0.964728 -0.343388 0.055728 -0.117661 -0.347914 0.053336 -0.139892 -0.341641 0.052257 -0.129927 -0.336395 0.047978 -0.120921 -0.321588 0.03046 -0.058404 --0.03278 -0.126524 0.032896 -0.123153 -0.030496 -0.92279 --0.219528 -0.064755 0.355362 -0.17326 -0.039216 0.310118 -0.165356 -0.036284 0.329203 -0.160346 -0.043597 0.347437 -0.160629 -0.032842 0.345031 -0.155372 -0.028368 0.356235 -0.150337 -0.035624 0.365792 -0.150665 -0.022157 0.367637 -0.154294 -0.010779 0.370781 -0.164253 -0.002173 0.375198 -0.035841 0.029887 0.534583 -0.079019 0.149079 0.400081 -0.349098 0.041786 -0.144112 -0.356004 0.042531 -0.154123 --0.099083 -0.124611 -0.660924 --0.113162 -0.120096 -0.750675 --0.104916 -0.111415 -0.751447 --0.102164 -0.117278 -0.708171 --0.086684 -0.110071 -0.660427 -0.084782 0.150209 0.412004 -0.089822 0.149412 0.422 -0.097534 0.153991 0.414529 -0.212494 -0.063496 0.322903 -0.108072 -0.017674 -0.704252 -0.085284 0.006734 0.70766 -0.013054 -0.070632 0.772858 --0.359252 0.073551 -0.158881 -0.35586 0.115447 -0.131675 -0.347078 0.121777 -0.131765 -0.11799 -0.014622 -0.948338 -0.118626 -0.022811 -0.937492 -0.077762 0.003004 0.692549 -0.149496 0.118045 -0.971237 -0.353272 0.046773 -0.161503 --0.169398 -0.075532 -0.842498 --0.166691 -0.077802 -0.88187 --0.169032 -0.085956 -0.842673 -0.17116 -0.100231 -0.965211 -0.078337 0.029774 0.697426 -0.078949 0.032083 0.70963 -0.078269 0.042027 0.723896 -0.081211 0.025526 0.724481 -0.081294 0.020966 0.742447 -0.081482 0.008499 0.724226 -0.079611 -0.010056 0.725374 -0.078703 -2e-05 0.707557 --0.212613 -0.063458 0.322876 --0.200714 -0.062423 0.31826 --0.201639 -0.056854 0.293115 --0.189474 -0.05766 0.314342 --0.18008 -0.049671 0.311534 -0.009779 -0.013603 0.811873 --0.348227 0.066029 -0.138773 --0.188471 -0.031806 0.264632 --0.192304 -0.023168 0.243687 --0.199008 -0.02674 0.227711 -0.195543 0.005391 -0.970995 --0.001704 -0.064566 0.188111 -0.123955 0.115992 0.451438 -0.130797 0.094938 0.458999 --0.244484 0.024749 0.252048 --0.229914 0.023071 0.265249 --0.22803 0.018149 0.286204 -0.181303 -0.06694 0.357393 -0.198046 -0.066846 0.34082 -0.192095 -0.072159 0.365611 -0.200594 -0.062462 0.318287 -0.182369 -0.078638 0.387568 -0.170041 -0.086435 0.410617 -0.165672 -0.076658 0.390767 -0.150865 -0.068843 0.390208 -0.164921 -0.06856 0.372738 -0.169569 -0.061516 0.35465 -0.163363 -0.053478 0.350949 -0.175072 -0.056658 0.334159 -0.179961 -0.04971 0.311561 -0.189355 -0.0577 0.314369 -0.135647 0.09831 -0.976539 -0.141277 0.10969 -0.974611 -0.126106 0.115455 -0.971237 -0.109014 0.105489 -0.967742 -0.224509 0.002044 0.362953 -0.23563 0.000737 0.339605 -0.245713 -0.001086 0.317242 --0.233957 -0.054354 0.332434 --0.224097 -0.06067 0.327773 -0.144342 -0.12144 -0.974756 -0.154109 -0.119068 -0.973887 -0.154443 -0.109248 -0.978226 -0.163545 -0.107182 -0.975334 -0.170636 -0.091241 -0.975334 -0.119337 0.007039 0.530859 -0.178109 -0.018702 0.526682 -0.164945 -0.013349 0.526722 -0.162346 0.000925 0.509971 -0.151118 -0.002354 0.520824 -0.134781 0.020876 0.511031 -0.132609 0.005697 0.523097 --0.306945 0.042229 0.09751 --0.303168 0.051644 0.074662 --0.291433 0.05709 0.098532 --0.296602 0.06151 0.059517 --0.284876 0.064238 0.058154 --0.303535 0.067031 0.025178 --0.310169 0.071885 0.004791 --0.313972 0.059722 0.00451 --0.316999 0.047501 -0.000922 --0.304492 0.018549 0.147779 --0.306017 0.030089 0.12688 --0.299644 0.043425 0.128327 --0.290972 0.046839 0.150848 --0.289846 0.052813 0.128654 --0.288193 0.024543 0.197143 --0.290501 0.036632 0.173892 --0.297647 0.021679 0.172269 --0.278244 0.048375 0.172783 --0.275074 0.025804 0.220254 --0.260374 0.027071 0.23891 --0.262818 0.037546 0.219144 --0.263681 0.048027 0.194925 -0.212654 0.002962 0.390454 -0.220072 -0.007654 0.391584 -0.213481 -0.011706 0.414323 -0.119387 -0.116328 0.050137 -0.110117 -0.123127 0.032005 -0.084289 -0.133045 0.031941 -0.097998 -0.127538 0.015318 -0.09455 0.144916 0.431457 --0.055893 0.052902 0.614078 --0.050027 0.052827 0.603861 -0.188938 0.01062 0.403933 -0.202273 0.007998 0.397072 -0.206061 -0.00109 0.413194 --0.085415 0.006678 0.707696 --0.086296 0.000176 0.696959 --0.183198 -0.04117 0.287604 --0.173379 -0.039177 0.310091 -0.205214 -0.008175 0.434878 -0.195277 0.000444 0.451765 -0.207934 -0.017726 0.451241 -0.209911 -0.026981 0.471619 -0.214345 -0.036924 0.448785 -0.213816 -0.055074 0.444661 -0.025324 -0.058631 0.7826 -0.01253 -0.061062 0.78584 -0.011407 -0.050197 0.794688 -0.206377 -0.070783 0.44007 -0.192742 -0.084742 0.435948 -0.180747 -0.093837 0.455606 -0.175672 -0.091547 0.432608 -0.157013 -0.090462 0.43041 -0.355823 0.129013 -0.15251 -0.167365 0.004612 0.390041 -0.154985 0.004237 0.403646 -0.172113 0.009406 0.407158 -0.174958 0.013221 0.428688 --0.202617 0.011735 0.352242 --0.359857 0.065634 -0.137145 --0.358469 0.06381 -0.148531 -0.339127 0.109041 -0.111258 --0.355676 0.07573 -0.135227 --0.361979 0.072975 -0.149342 -0.100875 0.083121 -0.970754 -0.103018 0.096223 -0.970272 -0.115575 0.098697 -0.974129 -0.124166 0.086266 -0.97678 -0.131507 0.068883 -0.977745 -0.111941 0.077113 -0.975093 -0.208017 -0.065919 0.46385 --0.078302 0.029706 0.697437 -0.058903 -0.072872 -0.54404 -0.057457 -0.067087 -0.57373 -0.063343 -0.085673 -0.573707 -0.055864 -0.050678 -0.574756 -0.073186 -0.104666 -0.575196 -0.086227 -0.121112 -0.577144 -0.078229 -0.107652 -0.546591 -0.084535 -0.107544 -0.520092 -0.072452 -0.093879 -0.518952 -0.062968 -0.078092 -0.517891 -0.057687 -0.062526 -0.518367 -0.058442 -0.052134 -0.497397 -0.056341 -0.045931 -0.519415 -0.055964 -0.039868 -0.546115 -0.058107 -0.034764 -0.575973 --0.245354 -0.033827 0.339365 --0.2413 -0.045081 0.336438 -0.341625 0.09724 -0.100812 -0.129441 -0.119864 -0.525052 -0.139869 -0.126175 -0.553229 -0.143561 -0.113256 -0.525748 -0.134261 -0.135387 -0.581649 -0.149939 -0.128617 -0.58101 -0.163143 -0.115538 -0.579851 -0.173515 -0.097078 -0.578243 -0.165684 -0.101575 -0.551639 -0.165725 -0.086894 -0.52567 -0.156171 -0.101734 -0.525316 -0.113951 -0.122268 -0.523613 -0.099083 -0.11872 -0.522086 -0.107925 -0.129584 -0.550956 -0.101374 -0.131867 -0.579516 -0.117808 -0.136392 -0.580816 -0.141849 0.001235 0.469012 -0.15723 0.009821 0.447299 -0.17877 0.011883 0.450648 -0.18376 0.006719 0.473145 --0.06438 0.023247 0.636874 --0.063112 0.03358 0.628538 --0.205128 -0.037064 0.23151 --0.204654 -0.043252 0.248828 --0.214134 -0.045326 0.236279 --0.194701 -0.041565 0.265974 --0.203749 -0.049259 0.268678 --0.221296 -0.04775 0.22812 --0.216327 -0.041007 0.217945 --0.211949 -0.032031 0.203072 --0.2062 -0.033706 0.219456 -0.176307 -0.053861 -0.970754 -0.174371 -0.07259 -0.973405 -0.166037 -0.0826 -0.978226 -0.1554 -0.093877 -0.979672 -0.156574 -0.072029 -0.977504 -0.159115 -0.051027 -0.974611 -0.173162 -0.027178 -0.973164 -0.170514 -0.042869 -0.972683 -0.180207 -0.035449 -0.968585 -0.186921 -0.015395 -0.969067 --0.074434 -0.029724 -0.656313 --0.086129 -0.032795 -0.703283 --0.091303 -0.048692 -0.74926 --0.088386 -0.060223 -0.749972 --0.097276 -0.061724 -0.795373 --0.19108 -0.014562 0.512553 -0.091317 -0.106014 -0.49759 -0.172606 0.006961 0.492825 -0.153428 0.00626 0.488919 -0.145335 0.048119 0.463165 -0.141073 0.041311 0.48148 -0.128945 0.062822 0.483894 -0.131145 0.042934 0.49661 -0.190494 -0.014589 0.511124 -0.201084 -0.01873 0.493017 -0.187589 -0.0032 0.494019 -0.181753 -0.041215 -0.578804 -0.177671 -0.025978 -0.580003 -0.182778 -0.057623 -0.577679 -0.185387 -0.052529 -0.608923 -0.18629 -0.064883 -0.643856 -0.184542 -0.048934 -0.6447 -0.179789 -0.033866 -0.646825 -0.170847 -0.021688 -0.649466 -0.176552 -0.022224 -0.612247 -0.169293 -0.012787 -0.580641 -0.155768 -0.003262 -0.581065 -0.172059 -0.068744 -0.526163 -0.178575 -0.063213 -0.551391 -0.180073 -0.076866 -0.577502 --0.360641 0.10223 -0.157401 --0.362883 0.096396 -0.150932 -0.183637 -0.083834 -0.644886 -0.18003 -0.090725 -0.609774 -0.149608 -0.004021 -0.614175 -0.33473 0.104244 -0.099942 -0.064528 -0.030949 -0.614326 -0.074879 -0.029935 -0.657674 -0.061988 -0.042839 -0.478018 -0.065538 -0.034823 -0.462206 -0.095934 -0.011351 -0.657415 -0.08391 -0.018958 -0.65811 --0.362464 0.103486 -0.146037 -0.362126 0.105398 -0.155069 --0.096585 -0.099221 -0.751664 --0.091146 -0.087026 -0.751883 --0.084203 -0.088625 -0.70766 --0.07074 -0.075656 -0.659033 --0.077055 -0.093614 -0.659699 --0.071779 -0.099456 -0.613822 --0.088308 -0.072542 -0.75043 -0.008712 0.092262 0.790131 -0.025323 0.069293 0.800772 -0.017018 0.080849 0.797134 -0.024425 0.08945 0.787811 -0.039578 0.084327 0.7826 -0.0407 0.065028 0.794688 -0.181339 -0.009278 -0.973165 -0.170214 -0.002734 -0.973887 -0.15423 0.009263 -0.972683 -0.010846 -0.039475 0.801916 -0.010059 -0.027181 0.807399 -0.019509 -0.009221 0.810943 --0.189676 -0.02732 0.526138 -0.157428 -0.013009 -0.65097 -0.142149 -0.008196 -0.653256 -0.126057 -0.006072 -0.654604 -0.111627 -0.007039 -0.656616 --0.168201 -0.079358 -0.908568 -0.153661 -0.045747 0.361072 -0.021957 -0.035043 0.800794 --0.245751 -0.02161 0.340947 --0.252311 -0.024394 0.317072 --0.256938 -0.015188 0.293823 -0.315719 0.098469 -0.030418 -0.323652 0.112064 -0.054205 -0.069311 -0.035047 -0.448088 --0.225878 -0.01958 0.394647 --0.23702 -0.020202 0.367703 --0.242451 -0.009717 0.34102 --0.06082 0.050615 0.625212 --0.238481 0.01372 0.289848 --0.22739 0.014637 0.311703 --0.226356 0.008767 0.336769 --0.215117 0.013529 0.332851 --0.23575 0.000777 0.339577 --0.245832 -0.001047 0.317215 --0.247528 0.006025 0.292552 --0.253759 -0.003734 0.293895 --0.261233 -0.004006 0.273759 --0.22018 -0.00796 0.391069 --0.224623 0.001911 0.362682 -0.010481 0.086772 0.577044 -0.143699 -0.044555 0.377004 --0.251061 0.011926 0.271567 --0.070169 -0.043554 -0.657136 --0.068323 -0.059671 -0.657643 --0.078948 -0.058156 -0.70482 -0.138631 -0.069856 0.409434 -0.140603 -0.056375 0.391026 -0.144295 0.068666 0.390136 -0.131039 0.072281 0.373987 -0.119545 0.061258 0.365376 -0.148392 0.060229 0.409178 -0.14791 0.06047 0.427255 -0.061194 -0.101996 0.128584 -0.068223 -0.115659 0.112662 -0.055213 -0.122019 0.097083 -0.078208 -0.122298 0.098267 -0.090109 -0.125325 0.083517 -0.099539 -0.114531 0.09685 -0.119083 -0.100139 0.094385 -0.130689 -0.095004 0.080497 -0.133536 -0.081117 0.092084 -0.14342 -0.057279 0.089751 -0.079745 -0.131592 0.067127 -0.103922 -0.122391 0.067669 -0.055395 -0.131208 0.065896 -0.069381 -0.135449 0.049532 -0.124957 -0.10646 0.067114 -0.036158 -0.114652 0.094315 -0.042764 -0.124662 0.081103 -0.033238 -0.101133 0.108597 -0.045264 -0.094838 0.127286 -0.206129 -0.058717 0.486915 -0.197849 -0.077068 0.482803 -0.184214 -0.091027 0.478681 -0.140409 -0.081699 0.429785 -0.127904 0.080476 0.470882 -0.137787 0.071317 0.459794 -0.145259 0.063363 0.446056 --0.26065 0.006429 0.260385 -0.119403 -0.006611 -0.956534 --0.25518 0.018034 0.254722 -0.01688 -0.065543 0.196797 -0.03084 -0.067719 0.230546 -0.110934 -0.109238 -0.937974 -0.116602 -0.118716 -0.954292 -0.038681 -0.056487 0.774999 -0.036323 -0.044192 0.787834 -0.046088 -0.027895 0.790202 -0.034078 -0.03147 0.797181 -0.031518 -0.017656 0.804089 -0.142881 -0.10762 -0.979672 -0.073673 0.057384 0.724556 -0.074239 0.055939 0.737636 -0.133232 0.051032 -0.977021 -0.332002 0.116825 -0.076179 -0.059894 0.044442 0.78584 -0.051924 0.056737 0.789578 -0.051946 0.0401 0.794742 -0.047547 0.071461 0.78584 -0.047029 0.020604 0.800903 -0.039958 0.001019 0.804143 -0.050712 0.005504 0.797234 -0.054282 -0.009597 0.790202 -0.060455 0.00956 0.787834 -0.069883 0.012992 0.774999 -0.06472 0.029003 0.7826 -0.065112 0.048174 0.775876 -0.070107 0.051334 0.763795 -0.061801 0.065488 0.769652 -0.05546 0.081657 0.7638 -0.052205 0.076479 0.775876 -0.057184 0.084832 0.751847 -0.029857 -0.004128 0.808754 --0.210827 -0.020354 0.184299 --0.213135 -0.008264 0.161048 -0.125472 -0.004017 -0.963041 -0.077122 0.035307 0.756062 -0.07301 0.053779 0.751837 -0.066428 0.071108 0.745741 -0.074148 -0.002592 0.762786 -0.077291 0.016137 0.760543 --0.240427 -0.009453 0.11697 --0.226254 -0.009526 0.137936 -0.07628 -0.016315 0.745837 --0.108726 0.077671 -0.08699 --0.147896 0.060634 -0.08722 --0.130339 0.072406 -0.087051 --0.120429 0.074217 -0.051083 --0.245918 0.004612 0.0982 -0.118984 -0.121476 -0.966898 -0.130897 -0.125393 -0.971237 -0.135425 -0.118096 -0.97678 -0.13368 0.025991 -0.973647 -0.136093 -0.000389 -0.968586 -0.207495 -0.037927 0.490559 -0.051363 -0.053485 0.762786 -0.071824 0.052174 -0.311375 --0.109144 0.070674 -0.018124 --0.068987 -0.135429 0.049518 --0.079129 -0.131615 0.067054 --0.102803 -0.122492 0.067471 --0.057114 -0.134223 0.032887 --0.044072 -0.130639 0.018188 -0.058097 -0.024607 0.778987 -0.068536 -0.021033 0.764406 -0.061016 -0.037903 0.764406 diff --git a/src/cython/example/ex_clustering.py b/src/cython/example/ex_clustering.py deleted file mode 100644 index 44b3d610..00000000 --- a/src/cython/example/ex_clustering.py +++ /dev/null @@ -1,64 +0,0 @@ -import numpy as np -from sklearn.metrics import pairwise_distances -import os -import gudhi as gd -import sys -sys.path.append("../sktda/") -from clustering import * - -X = np.loadtxt("../../../data/points/human") - -print("Mapper computation with point cloud") -mapper = MapperComplex(inp="point cloud", - filters=X[:,[2,0]], - filter_bnds=np.array([[np.nan,np.nan],[np.nan,np.nan]]), - resolutions=np.array([np.nan,np.nan]), gains=np.array([0.33,0.33]), - colors=X[:,2:3], - ).fit(X) - -f = open("mapper_pc", "w") -f.write("%s\n%s\n%s\n%f %f\n%d %d\n" % ("human", "coord2-0", "coord2", 10, 0.33, len(mapper.mapper_.get_skeleton(0)), len([edge for (edge,f) in mapper.mapper_.get_skeleton(1) if len(edge)==2]))) -for (vertex,_) in mapper.mapper_.get_skeleton(0): - f.write(str(vertex[0]) + " " + str(mapper.node_info_[vertex[0]]["colors"][0]) + " " + str(mapper.node_info_[vertex[0]]["size"]) + "\n") -for (edge,_) in mapper.mapper_.get_skeleton(1): - if len(edge) == 2: - f.write(str(edge[0]) + " " + str(edge[1]) + "\n") -f.close() - -os.system("python3 ~/Git/gudhi-devel/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py -f ~/Git/gudhi-devel/src/cython/example/mapper_pc") -os.system("rm ~/Git/gudhi-devel/src/cython/example/mapper_pc") - -dgms = mapper.compute_persistence_diagrams() -plot = gd.plot_persistence_diagram(dgms[0]) -plot.show() - -distrib = mapper.compute_distribution(X, N=10) -print("Distance threshold associated to confidence 90 percent is " + str(distrib[int(np.floor(0.9 * len(distrib)))])) - -print("Mapper computation with pairwise distances only") -X = pairwise_distances(X) -mapper = MapperComplex(inp="distance matrix", - filters=X[:,[2,0]], - filter_bnds=np.array([[np.nan,np.nan],[np.nan,np.nan]]), - resolutions=np.array([np.nan,np.nan]), gains=np.array([0.33,0.33]), - colors=np.max(X, axis=1)[:,np.newaxis], - ).fit(X) - -f = open("mapper_dm", "w") -f.write("%s\n%s\n%s\n%f %f\n%d %d\n" % ("human", "coord2-0", "coord2", 10, 0.33, len(mapper.mapper_.get_skeleton(0)), len([edge for (edge,f) in mapper.mapper_.get_skeleton(1) if len(edge)==2]))) -for (vertex,_) in mapper.mapper_.get_skeleton(0): - f.write(str(vertex[0]) + " " + str(mapper.node_info_[vertex[0]]["colors"][0]) + " " + str(mapper.node_info_[vertex[0]]["size"]) + "\n") -for (edge,_) in mapper.mapper_.get_skeleton(1): - if len(edge) == 2: - f.write(str(edge[0]) + " " + str(edge[1]) + "\n") -f.close() - -os.system("python3 ~/Git/gudhi-devel/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py -f ~/Git/gudhi-devel/src/cython/example/mapper_dm") -os.system("rm ~/Git/gudhi-devel/src/cython/example/mapper_dm") - -dgms = mapper.compute_persistence_diagrams() -plot = gd.plot_persistence_diagram(dgms[0]) -plot.show() - -distrib = mapper.compute_distribution(X, N=10) -print("Distance threshold associated to confidence 90 percent is " + str(distrib[int(np.floor(0.9 * len(distrib)))])) diff --git a/src/cython/sktda/clustering.py b/src/cython/sktda/clustering.py deleted file mode 100644 index d3dd531b..00000000 --- a/src/cython/sktda/clustering.py +++ /dev/null @@ -1,269 +0,0 @@ -""" -@author: Mathieu Carriere -All rights reserved -""" - -import numpy as np -import itertools - -from metrics import BottleneckDistance -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.cluster import DBSCAN, AgglomerativeClustering -from sklearn.metrics import pairwise_distances -from sklearn.neighbors import radius_neighbors_graph, kneighbors_graph -from scipy.spatial.distance import directed_hausdorff -from scipy.sparse import csgraph - -try: - import gudhi as gd - USE_GUDHI = True - -except ImportError: - USE_GUDHI = False - print("Gudhi not found: MapperComplex not available") - -############################################# -# Clustering ################################ -############################################# - -class MapperComplex(BaseEstimator, TransformerMixin): - """ - This is a class for computing Mapper simplicial complexes on point clouds or distance matrices. - """ - def __init__(self, filters, filter_bnds, colors, resolutions, gains, inp="point cloud", clustering=DBSCAN(), mask=0): - """ - Constructor for the MapperComplex class. - - Attributes: - inp (string): either "point cloud" or "distance matrix". Specifies the type of input data. - filters (numpy array of shape (num_points) x (num_filters)): filters (sometimes called lenses) used to compute the Mapper. Each column of the numpy array defines a scalar function defined on the input points. - filter_bnds (numpy array of shape (num_filters) x 2): limits of each filter, of the form [[f_1^min, f_1^max], ..., [f_n^min, f_n^max]]. If one of the values is numpy.nan, it can be computed from the points with the fit() method. - colors (numpy array of shape (num_points) x (num_colors)): functions used to color the nodes of the output Mapper simplicial complex. More specifically, coloring is done by computing the means of these functions on the subpopulations corresponding to each node. It can be the same as filters. - resolutions (numpy array of shape num_filters containing integers): resolution of each filter, ie number of intervals required to cover each filter image. - gains (numpy array of shape num_filters containing doubles in [0,1]): gain of each filter, ie overlap percentage of the intervals covering each filter image. - clustering (class): clustering class (default sklearn.cluster.DBSCAN()). Common clustering classes can be found in the scikit-learn library (such as AgglomerativeClustering for instance). - mask (int): threshold on the size of the Mapper nodes (default 0). Any node associated to a subpopulation with less than **mask** points will be removed. - - mapper_ (gudhi SimplexTree): Mapper simplicial complex computed after calling the fit() method - node_info_ (dictionary): various information associated to the nodes of the Mapper. - """ - self.filters, self.filter_bnds, self.resolutions, self.gains, self.colors, self.clustering = filters, filter_bnds, resolutions, gains, colors, clustering - self.input, self.mask = inp, mask - - def get_optimal_parameters_for_agglomerative_clustering(self, X, beta=0., C=10., N=100): - """ - Compute optimal scale and resolutions for a point cloud or a distance matrix. - - Parameters: - X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. - beta (double): exponent parameter (default 0.). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. - C (double): constant parameter (default 10.). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. - N (int): subsampling iterations (default 100). See http://www.jmlr.org/papers/volume19/17-291/17-291.pdf for details. - - Returns: - delta (double): optimal scale that can be used with agglomerative clustering. - resolutions (numpy array of shape (num_filters): optimal resolutions associated to each filter. - """ - num_pts, num_filt, delta = X.shape[0], self.filters.shape[1], 0 - m = int( num_pts / np.exp((1+beta) * np.log(np.log(num_pts)/np.log(C))) ) - for _ in range(N): - subpop = np.random.choice(num_pts, size=m, replace=False) - if self.input == "point cloud": - d, _, _ = directed_hausdorff(X, X[subpop,:]) - if self.input == "distance matrix": - d = np.max(np.min(X[:,subpop], axis=1), axis=0) - delta += d/N - - pairwise = pairwise_distances(X, metric="euclidean") if self.input == "point cloud" else X - pairs = np.argwhere(pairwise <= delta) - num_pairs = pairs.shape[0] - res = [] - for f in range(num_filt): - F = self.filters[:,f] - minf, maxf = np.min(F), np.max(F) - resf = 0 - for p in range(num_pairs): - resf = max(resf, abs(F[pairs[p,0]] - F[pairs[p,1]])) - res.append(int((maxf-minf)/resf)) - - return delta, np.array(res) - - - def fit(self, X, y=None): - """ - Fit the MapperComplex class on a point cloud or a distance matrix: compute the Mapper and store it in a simplex tree called mapper_ - - Parameters: - X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. - y (n x 1 array): point labels (unused). - """ - num_pts, num_filters, num_colors = self.filters.shape[0], self.filters.shape[1], self.colors.shape[1] - - # If some resolutions are not specified, automatically compute them - if np.any(np.isnan(self.resolutions)): - delta, resolutions = self.get_optimal_parameters_for_agglomerative_clustering(X=X, beta=0., C=10, N=100) - #self.clustering = NNClustering(radius=delta, inp=self.input) - if self.input == "point cloud": - self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, affinity="euclidean") - else: - self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, affinity="precomputed") - self.resolutions = np.where(np.isnan(self.resolutions), resolutions, self.resolutions) - - # If some filter limits are unspecified, automatically compute them - self.filter_bnds = np.where(np.isnan(self.filter_bnds), np.hstack([np.min(self.filters, axis=0)[:,np.newaxis], np.max(self.filters, axis=0)[:,np.newaxis]]), self.filter_bnds) - - # Initialize attributes - self.mapper_, self.node_info_ = gd.SimplexTree(), {} - - # Compute which points fall in which patch or patch intersections - interval_inds, intersec_inds = np.empty(self.filters.shape), np.empty(self.filters.shape) - for i in range(num_filters): - f, r, g = self.filters[:,i], self.resolutions[i], self.gains[i] - min_f, max_f = self.filter_bnds[i,0], np.nextafter(self.filter_bnds[i,1], np.inf) - interval_endpoints, l = np.linspace(min_f, max_f, num=r+1, retstep=True) - intersec_endpoints = [] - for j in range(1, len(interval_endpoints)-1): - intersec_endpoints.append(interval_endpoints[j] - g*l / (2 - 2*g)) - intersec_endpoints.append(interval_endpoints[j] + g*l / (2 - 2*g)) - interval_inds[:,i] = np.digitize(f, interval_endpoints) - intersec_inds[:,i] = 0.5 * (np.digitize(f, intersec_endpoints) + 1) - - # Build the binned_data map that takes a patch or a patch intersection and outputs the indices of the points contained in it - binned_data = {} - for i in range(num_pts): - list_preimage = [] - for j in range(num_filters): - a, b = interval_inds[i,j], intersec_inds[i,j] - list_preimage.append([a]) - if b == a: - list_preimage[j].append(a+1) - if b == a-1: - list_preimage[j].append(a-1) - list_preimage = list(itertools.product(*list_preimage)) - for pre_idx in list_preimage: - try: - binned_data[pre_idx].append(i) - except KeyError: - binned_data[pre_idx] = [i] - - # Initialize the cover map, that takes a point and outputs the clusters to which it belongs - cover, clus_base = [[] for _ in range(num_pts)], 0 - - # For each patch - for preimage in binned_data: - - # Apply clustering on the corresponding subpopulation - idxs = np.array(binned_data[preimage]) - if len(idxs) > 1: - clusters = self.clustering.fit_predict(X[idxs,:]) if self.input == "point cloud" else self.clustering.fit_predict(X[idxs,:][:,idxs]) - elif len(idxs) == 1: - clusters = np.array([0]) - else: - continue - - # Collect various information on each cluster - num_clus_pre = np.max(clusters) + 1 - for clus_i in range(num_clus_pre): - node_name = clus_base + clus_i - subpopulation = idxs[clusters == clus_i] - if len(subpopulation) >= self.mask: - self.node_info_[node_name] = {} - self.node_info_[node_name]["indices"] = subpopulation - self.node_info_[node_name]["size"] = len(subpopulation) - self.node_info_[node_name]["colors"] = np.mean(self.colors[subpopulation,:], axis=0) - self.node_info_[node_name]["patch"] = preimage - - # Update the cover map - for pt in range(clusters.shape[0]): - node_name = clus_base + clusters[pt] - if clusters[pt] != -1 and self.node_info_[node_name]["size"] >= self.mask: - cover[idxs[pt]].append(node_name) - - clus_base += np.max(clusters) + 1 - - # Insert the simplices of the Mapper complex - for i in range(num_pts): - self.mapper_.insert(cover[i], filtration=-3) - self.mapper_.initialize_filtration() - - return self - - def compute_persistence_diagrams(self): - """ - Compute the extended persistence diagrams of the Mapper simplicial complex associated to each color function. - - Returns: - list_dgm (list of gudhi persistence diagrams): output extended persistence diagrams. There is one per color function. - """ - num_cols, list_dgm = self.colors.shape[1], [] - - # Compute an extended persistence diagram for each color - for c in range(num_cols): - - # Retrieve all color values - col_vals = {node_name: self.node_info_[node_name]["colors"][c] for node_name in self.node_info_.keys()} - - # Create a new simplicial complex by coning the Mapper with an extra point with name -2 - st = gd.SimplexTree() - list_simplices, list_vertices = self.mapper_.get_skeleton(1), self.mapper_.get_skeleton(0) - for (simplex, f) in list_simplices: - st.insert(simplex + [-2], filtration=-3) - - # Assign ascending filtration values on the original simplices and descending filtration values on the coned simplices - min_val, max_val = min(col_vals), max(col_vals) - for (vertex, f) in list_vertices: - if st.find(vertex): - st.assign_filtration(vertex, filtration = -2 + (col_vals[vertex[0]]-min_val)/(max_val-min_val)) - st.assign_filtration(vertex + [-2], filtration = 2 - (col_vals[vertex[0]]-min_val)/(max_val-min_val)) - - # Compute persistence - st.make_filtration_non_decreasing() - dgm = st.persistence() - - # Output extended persistence diagrams - for point in range(len(dgm)): - b,d = dgm[point][1][0], dgm[point][1][1] - b,d = min_val+(2-abs(b))*(max_val-min_val), min_val+(2-abs(d))*(max_val-min_val) - dgm[point] = tuple([dgm[point][0], tuple([b,d])]) - list_dgm.append(dgm) - - return list_dgm - - def compute_distribution(self, X, N=100): - """ - Compute a bootstrap distribution of bottleneck distances. More specifically, subsample the input point cloud or distance matrix, compute the Mapper with the same parameters on this subsample, and compare its extended persistence diagrams with the original ones. - - Parameters: - X (numpy array of shape (num_points) x (num_coordinates) if point cloud and (num_points) x (num_points) if distance matrix): input point cloud or distance matrix. - N (int): bootstrap iterations (default 100). - - Returns: - distribution: list of bottleneck distance values. - """ - num_pts, distribution = len(X), [] - dgm = self.compute_persistence_diagrams() - - for bootstrap_id in range(N): - - print(str(bootstrap_id) + "th iteration") - - # Randomly select points - idxs = np.random.choice(num_pts, size=num_pts, replace=True) - Xboot = X[idxs,:] if self.input == "point cloud" else X[idxs,:][:,idxs] - f_boot, c_boot = self.filters[idxs,:], self.colors[idxs,:] - Mboot = self.__class__(filters=f_boot, filter_bnds=self.filter_bnds, colors=c_boot, resolutions=self.resolutions, gains=self.gains, inp=self.input, clustering=self.clustering).fit(Xboot) - - # Compute the corresponding persistence diagrams - dgm_boot = Mboot.compute_persistence_diagrams() - - # Compute the bottleneck distances between them and keep the maximum - df = 0. - for i in range(len(dgm)): - npts, npts_boot = len(dgm[i]), len(dgm_boot[i]) - D1 = np.array([[dgm[i][pt][1][0], dgm[i][pt][1][1]] for pt in range(npts) if dgm[i][pt][0] <= 1]) - D2 = np.array([[dgm_boot[i][pt][1][0], dgm_boot[i][pt][1][1]] for pt in range(npts_boot) if dgm_boot[i][pt][0] <= 1]) - bottle = BottleneckDistance().fit([D1]) - df = max(df, float(np.squeeze(bottle.transform([D2])))) - distribution.append(df) - - return np.sort(distribution) -- cgit v1.2.3 From c18017f78779239cf17dc1a9b0d00a9b8f075a2d Mon Sep 17 00:00:00 2001 From: mathieu Date: Mon, 9 Sep 2019 09:50:23 -0400 Subject: fixed a few typos and added entropy --- src/cython/sktda/kernel_methods.py | 2 +- src/cython/sktda/metrics.py | 18 +++++--- src/cython/sktda/preprocessing.py | 16 +++---- src/cython/sktda/vector_methods.py | 93 ++++++++++++++++++++++++++++++++++---- 4 files changed, 106 insertions(+), 23 deletions(-) diff --git a/src/cython/sktda/kernel_methods.py b/src/cython/sktda/kernel_methods.py index 57bfafd7..6e3f6d5e 100644 --- a/src/cython/sktda/kernel_methods.py +++ b/src/cython/sktda/kernel_methods.py @@ -6,7 +6,7 @@ All rights reserved import numpy as np from sklearn.base import BaseEstimator, TransformerMixin from sklearn.metrics import pairwise_distances -from metrics import SlicedWassersteinDistance, PersistenceFisherDistance +from .metrics import SlicedWassersteinDistance, PersistenceFisherDistance ############################################# # Kernel methods ############################ diff --git a/src/cython/sktda/metrics.py b/src/cython/sktda/metrics.py index 05141e8b..5dc219cd 100644 --- a/src/cython/sktda/metrics.py +++ b/src/cython/sktda/metrics.py @@ -192,7 +192,7 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) - Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) Xfit[j,i] = Xfit[i,j] else: Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) @@ -200,7 +200,7 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) - Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) Xfit[j,i] = Xfit[i,j] else: projection = (1./2) * np.ones((2,2)) @@ -214,13 +214,19 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) - vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) - Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + if np.sum(vectori) != 0: + vectori = vectori/np.sum(vectori) + if np.sum(vectorj) != 0: + vectorj = vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) else: Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) - Xfit[i,j] = np.arccos(np.dot(np.sqrt(vectori), np.sqrt(vectorj))) + if np.sum(vectori) != 0: + vectori = vectori/np.sum(vectori) + if np.sum(vectorj) != 0: + vectorj = vectorj/np.sum(vectorj) + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) return Xfit diff --git a/src/cython/sktda/preprocessing.py b/src/cython/sktda/preprocessing.py index 2f4a5fe5..b2bc68f6 100644 --- a/src/cython/sktda/preprocessing.py +++ b/src/cython/sktda/preprocessing.py @@ -26,8 +26,8 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). + X (n x 2 numpy array): input persistence diagram. + y (n x 1 array): persistence diagram label (unused). """ return self @@ -36,10 +36,10 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): Apply the BirthPersistenceTransform function on the persistence diagrams. Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + X (n x 2 numpy array): input persistence diagram. Returns: - Xfit (list of n x 2 numpy arrays): transformed persistence diagrams. + Xfit (n x 2 numpy array): transformed persistence diagram. """ Xfit = np.matmul(X, np.array([[1., -1.],[0., 1.]])) return Xfit @@ -55,10 +55,10 @@ class DiagramPreprocessor(BaseEstimator, TransformerMixin): Attributes: use (bool): whether to use the class or not (default False). - scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). + scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). """ - self.scalers = scalers - self.use = use + self.scalers = scalers + self.use = use def fit(self, X, y=None): """ @@ -74,7 +74,7 @@ class DiagramPreprocessor(BaseEstimator, TransformerMixin): else: P = np.concatenate(X,0) for (indices, scaler) in self.scalers: - scaler.fit(P[:,indices]) + scaler.fit(np.reshape(P[:,indices], [-1, 1])) return self def transform(self, X): diff --git a/src/cython/sktda/vector_methods.py b/src/cython/sktda/vector_methods.py index 4dd147e7..99c2f420 100644 --- a/src/cython/sktda/vector_methods.py +++ b/src/cython/sktda/vector_methods.py @@ -5,10 +5,10 @@ All rights reserved import numpy as np from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.preprocessing import MinMaxScaler +from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler from sklearn.neighbors import DistanceMetric -from preprocessing import DiagramPreprocessor +from .preprocessing import DiagramPreprocessor, BirthPersistenceTransform ############################################# # Finite Vectorization methods ############## @@ -40,7 +40,8 @@ class PersistenceImage(BaseEstimator, TransformerMixin): y (n x 1 array): persistence diagram labels (unused). """ if np.isnan(np.array(self.im_range)).any(): - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(new_X,y) [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) return self @@ -56,9 +57,11 @@ class PersistenceImage(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. """ num_diag, Xfit = len(X), [] + new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) + for i in range(num_diag): - diagram, num_pts_in_diag = X[i], X[i].shape[0] + diagram, num_pts_in_diag = new_X[i], X[i].shape[0] w = np.ones(num_pts_in_diag) for j in range(num_pts_in_diag): @@ -69,7 +72,8 @@ class PersistenceImage(BaseEstimator, TransformerMixin): image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(self.bandwidth*np.sqrt(2*np.pi)), 1) Xfit.append(image.flatten()[np.newaxis,:]) - Xfit = np.concatenate(Xfit,0) + + Xfit = np.concatenate(Xfit,0) return Xfit @@ -150,7 +154,8 @@ class Landscape(BaseEstimator, TransformerMixin): ls[k,j] = events[j][k] Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) - Xfit = np.concatenate(Xfit,0) + + Xfit = np.concatenate(Xfit,0) return Xfit @@ -227,7 +232,8 @@ class Silhouette(BaseEstimator, TransformerMixin): silhouette_value -= step_x Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) - Xfit = np.concatenate(Xfit, 0) + + Xfit = np.concatenate(Xfit, 0) return Xfit @@ -286,7 +292,78 @@ class BettiCurve(BaseEstimator, TransformerMixin): bc[k] += 1 Xfit.append(np.reshape(bc,[1,-1])) - Xfit = np.concatenate(Xfit, 0) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class Entropy(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. + """ + def __init__(self, mode="scalar", normalized=True, resolution=100, ent_range=[np.nan, np.nan]): + """ + Constructor for the Entropy class. + + Attributes: + mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). + normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". + resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". + ent_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". + """ + self.mode, self.normalized, self.resolution, self.ent_range = mode, normalized, resolution, ent_range + + def fit(self, X, y=None): + """ + Fit the Entropy class on a list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.ent_range)).any(): + pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + self.ent_range = np.where(np.isnan(np.array(self.ent_range)), np.array([mx, My]), np.array(self.ent_range)) + return self + + def transform(self, X): + """ + Compute the entropy for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**)): output entropy. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.ent_range[0], self.ent_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) + + for i in range(num_diag): + + orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] + new_diagram = DiagramPreprocessor(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] + + if self.mode == "scalar": + ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) + Xfit.append(np.array([[ent]])) + + else: + ent = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = orig_diagram[j,:] + min_idx = np.minimum(np.maximum(np.ceil((px - self.ent_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.ent_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) + if self.normalized: + ent = ent / np.linalg.norm(ent, ord=1) + Xfit.append(np.reshape(ent,[1,-1])) + + Xfit = np.concatenate(Xfit, 0) return Xfit -- cgit v1.2.3 From 5e8144e8ddb4a39f9f4e60a9fafb5cc57f96acac Mon Sep 17 00:00:00 2001 From: mathieu Date: Mon, 9 Sep 2019 16:17:28 -0400 Subject: reviews --- src/cython/sktda/kernel_methods.py | 2 +- src/cython/sktda/metrics.py | 36 +++++++---- src/cython/sktda/preprocessing.py | 100 ++++++++++++++++++----------- src/cython/sktda/vector_methods.py | 126 ++++++++++++++++++------------------- 4 files changed, 149 insertions(+), 115 deletions(-) diff --git a/src/cython/sktda/kernel_methods.py b/src/cython/sktda/kernel_methods.py index 6e3f6d5e..d90bf164 100644 --- a/src/cython/sktda/kernel_methods.py +++ b/src/cython/sktda/kernel_methods.py @@ -101,7 +101,7 @@ class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) Xfit[i,j] = np.sum(np.multiply(W, E)) - Xfit[j,i] = X[i,j] + Xfit[j,i] = Xfit[i,j] else: ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] if self.kernel_approx is not None: diff --git a/src/cython/sktda/metrics.py b/src/cython/sktda/metrics.py index 5dc219cd..18db432a 100644 --- a/src/cython/sktda/metrics.py +++ b/src/cython/sktda/metrics.py @@ -145,7 +145,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): class PersistenceFisherDistance(BaseEstimator, TransformerMixin): """ - This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See http://papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. """ def __init__(self, bandwidth=1., kernel_approx=None): """ @@ -190,8 +190,12 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): if self.kernel_approx is not None: Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) - vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) - vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) Xfit[j,i] = Xfit[i,j] else: @@ -199,7 +203,11 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori, vectorj = vectori/np.sum(vectori), vectorj/np.sum(vectorj) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) Xfit[j,i] = Xfit[i,j] else: @@ -213,20 +221,22 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): if self.kernel_approx is not None: Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) - vectori, vectorj = np.matmul(Z, U.T), np.matmul(Z, V.T) - if np.sum(vectori) != 0: - vectori = vectori/np.sum(vectori) - if np.sum(vectorj) != 0: - vectorj = vectorj/np.sum(vectorj) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) else: Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - if np.sum(vectori) != 0: - vectori = vectori/np.sum(vectori) - if np.sum(vectorj) != 0: - vectorj = vectorj/np.sum(vectorj) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) return Xfit diff --git a/src/cython/sktda/preprocessing.py b/src/cython/sktda/preprocessing.py index b2bc68f6..512b02f3 100644 --- a/src/cython/sktda/preprocessing.py +++ b/src/cython/sktda/preprocessing.py @@ -13,7 +13,7 @@ from sklearn.preprocessing import StandardScaler class BirthPersistenceTransform(BaseEstimator, TransformerMixin): """ - This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. It is a particular scaler for persistence diagram that can be given as input for the DiagramPreprocessor class. + This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. """ def __init__(self): """ @@ -26,8 +26,8 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). Parameters: - X (n x 2 numpy array): input persistence diagram. - y (n x 1 array): persistence diagram label (unused). + X (n x 2 numpy array): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). """ return self @@ -36,16 +36,56 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): Apply the BirthPersistenceTransform function on the persistence diagrams. Parameters: - X (n x 2 numpy array): input persistence diagram. + X (list of n x 2 numpy array): input persistence diagrams. Returns: - Xfit (n x 2 numpy array): transformed persistence diagram. + Xfit (list of n x 2 numpy array): transformed persistence diagrams. """ - Xfit = np.matmul(X, np.array([[1., -1.],[0., 1.]])) + Xfit = [] + for diag in X: + new_diag = np.empty(diag.shape) + np.copyto(new_diag, diag) + new_diag[:,1] = new_diag[:,1] - new_diag[:,0] + Xfit.append(new_diag) return Xfit +class Clamping(BaseEstimator, TransformerMixin): + """ + This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. + """ + def __init__(self, limit=np.inf): + """ + Constructor for the Clamping class. + + Attributes: + limit (double): clamping value (default np.inf). + """ + self.limit = limit + + def fit(self, X, y=None): + """ + Fit the Clamping class on a list of list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). + + Parameters: + X (list of numpy arrays of size n): input values. + y (n x 1 array): value labels (unused). + """ + return self + + def transform(self, X): + """ + Clamp each list of values individually. -class DiagramPreprocessor(BaseEstimator, TransformerMixin): + Parameters: + X (list of numpy arrays of size n): input list of list of values. + + Returns: + Xfit (list of numpy arrays of size n): output list of list of values. + """ + Xfit = [np.where(L >= self.limit, self.limit * np.ones(L.shape), L) for L in X] + return Xfit + +class DiagramScaler(BaseEstimator, TransformerMixin): """ This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. """ @@ -116,6 +156,7 @@ class Padding(BaseEstimator, TransformerMixin): X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ + self.max_pts = max([len(diag) for diag in X]) return self def transform(self, X): @@ -130,12 +171,9 @@ class Padding(BaseEstimator, TransformerMixin): """ if self.use: Xfit, num_diag = [], len(X) - max_card = max([len(diag) for diag in X]) for diag in X: - [num_pts, dim] = diag.shape - diag_pad = np.zeros([max_card, dim+1]) - diag_pad[:num_pts,:dim] = diag - diag_pad[:num_pts, dim] = np.ones(num_pts) + diag_pad = np.pad(diag, ((0,max(0, self.max_pts - diag.shape[0])), (0,1)), "constant", constant_values=((0,0),(0,0))) + diag_pad[:diag.shape[0],2] = np.ones(diag.shape[0]) Xfit.append(diag_pad) else: Xfit = X @@ -143,9 +181,9 @@ class Padding(BaseEstimator, TransformerMixin): class ProminentPoints(BaseEstimator, TransformerMixin): """ - This is a class for removing points that are close or far from the diagonal in persistence diagrams. + This is a class for removing points that are close or far from the diagonal in persistence diagrams. If persistence diagrams are n x 2 numpy arrays (i.e. persistence diagrams with ordinary features), points are ordered and thresholded by distance-to-diagonal. If persistence diagrams are n x 1 numpy arrays (i.e. persistence diagrams with essential features), points are not ordered and thresholded by first coordinate. """ - def __init__(self, use=False, num_pts=10, threshold=-1, location="upper", point_type="finite"): + def __init__(self, use=False, num_pts=10, threshold=-1, location="upper"): """ Constructor for the ProminentPoints class. @@ -154,13 +192,11 @@ class ProminentPoints(BaseEstimator, TransformerMixin): location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. - point_type (string): either "finite" if persistence diagrams are n x 2 numpy arrays, or "essential" if persistence diagrams are n x 1 numpy arrays (default "finite"). If "finite", points are ordered and thresholded by distance-to-diagonal. If "essential", points are ordered and thresholded by first coordinate. """ self.num_pts = num_pts self.threshold = threshold self.use = use self.location = location - self.point_type = point_type def fit(self, X, y=None): """ @@ -174,7 +210,7 @@ class ProminentPoints(BaseEstimator, TransformerMixin): def transform(self, X): """ - If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. If point_type == "essential", do the same with first coordinate instead of distance-to-diagonal. + If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. Parameters: X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. @@ -186,16 +222,16 @@ class ProminentPoints(BaseEstimator, TransformerMixin): Xfit, num_diag = [], len(X) for i in range(num_diag): diag = X[i] - if self.point_type == "finite": + if diag.shape[1] >= 2: if diag.shape[0] > 0: - pers = np.abs(np.matmul(diag[:,:2], [-1., 1.])) + pers = np.abs(diag[:,1] - diag[:,0]) idx_thresh = pers >= self.threshold - thresh_diag, thresh_pers = diag[idx_thresh.flatten()], pers[idx_thresh.flatten()] + thresh_diag, thresh_pers = diag[idx_thresh], pers[idx_thresh] sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) if self.location == "upper": new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh.flatten()] ], axis=0) + new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh] ], axis=0) else: new_diag = diag @@ -203,11 +239,11 @@ class ProminentPoints(BaseEstimator, TransformerMixin): if diag.shape[0] > 0: birth = diag[:,:1] idx_thresh = birth >= self.threshold - thresh_diag, thresh_birth = diag[idx_thresh.flatten()], birth[idx_thresh.flatten()] + thresh_diag, thresh_birth = diag[idx_thresh], birth[idx_thresh] if self.location == "upper": new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh.flatten()] ], axis=0) + new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh] ], axis=0) else: new_diag = diag @@ -254,21 +290,9 @@ class DiagramSelector(BaseEstimator, TransformerMixin): if self.use: Xfit, num_diag = [], len(X) if self.point_type == "finite": - for i in range(num_diag): - diag = X[i] - if diag.shape[0] != 0: - idx_fin = diag[:,1] != self.limit - Xfit.append(diag[idx_fin,:]) - else: - Xfit.append(diag) - if self.point_type == "essential": - for i in range(num_diag): - diag = X[i] - if diag.shape[0] != 0: - idx_ess = diag[:,1] == self.limit - Xfit.append(np.delete(diag,1,1)[idx_ess,:]) - else: - Xfit.append(np.delete(diag,1,1)) + Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] + else: + Xfit = [ diag[diag[:,1] == self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] else: Xfit = X return Xfit diff --git a/src/cython/sktda/vector_methods.py b/src/cython/sktda/vector_methods.py index 99c2f420..3862f815 100644 --- a/src/cython/sktda/vector_methods.py +++ b/src/cython/sktda/vector_methods.py @@ -8,7 +8,7 @@ from sklearn.base import BaseEstimator, TransformerMixin from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler from sklearn.neighbors import DistanceMetric -from .preprocessing import DiagramPreprocessor, BirthPersistenceTransform +from .preprocessing import DiagramScaler, BirthPersistenceTransform ############################################# # Finite Vectorization methods ############## @@ -40,15 +40,15 @@ class PersistenceImage(BaseEstimator, TransformerMixin): y (n x 1 array): persistence diagram labels (unused). """ if np.isnan(np.array(self.im_range)).any(): - new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(new_X,y) - [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ + new_X = BirthPersistenceTransform().fit_transform(X) + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(new_X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) return self def transform(self, X): """ - Compute the persistence image for each persistence diagram individually and concatenate the results. + Compute the persistence image for each persistence diagram individually and store the results in a single numpy array. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. @@ -57,13 +57,13 @@ class PersistenceImage(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. """ num_diag, Xfit = len(X), [] - new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) + new_X = BirthPersistenceTransform().fit_transform(X) for i in range(num_diag): diagram, num_pts_in_diag = new_X[i], X[i].shape[0] - w = np.ones(num_pts_in_diag) + w = np.empty(num_pts_in_diag) for j in range(num_pts_in_diag): w[j] = self.weight(diagram[j,:]) @@ -81,29 +81,29 @@ class Landscape(BaseEstimator, TransformerMixin): """ This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. """ - def __init__(self, num_landscapes=5, resolution=100, ls_range=[np.nan, np.nan]): + def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): """ Constructor for the Landscape class. Attributes: num_landscapes (int): number of piecewise-linear functions to output (default 5). resolution (int): number of sample for all piecewise-linear functions (default 100). - ls_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ - self.num_landscapes, self.resolution, self.ls_range = num_landscapes, resolution, ls_range + self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range def fit(self, X, y=None): """ - Fit the Landscape class on a list of persistence diagrams: if any of the values in **ls_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + Fit the Landscape class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ - if np.isnan(np.array(self.ls_range)).any(): - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ - self.ls_range = np.where(np.isnan(np.array(self.ls_range)), np.array([mx, My]), np.array(self.ls_range)) + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) return self def transform(self, X): @@ -117,7 +117,7 @@ class Landscape(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**)): output persistence landscapes. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.ls_range[0], self.ls_range[1], self.resolution) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) step_x = x_values[1] - x_values[0] for i in range(num_diag): @@ -131,19 +131,19 @@ class Landscape(BaseEstimator, TransformerMixin): events.append([]) for j in range(num_pts_in_diag): - [px,py] = diagram[j,:] - min_idx = np.minimum(np.maximum(np.ceil((px - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.ls_range[0]) / step_x).astype(int), 0), self.resolution) + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) if min_idx < self.resolution and max_idx > 0: - landscape_value = self.ls_range[0] + min_idx * step_x - px + landscape_value = self.sample_range[0] + min_idx * step_x - px for k in range(min_idx, mid_idx): events[k].append(landscape_value) landscape_value += step_x - landscape_value = py - self.ls_range[0] - mid_idx * step_x + landscape_value = py - self.sample_range[0] - mid_idx * step_x for k in range(mid_idx, max_idx): events[k].append(landscape_value) landscape_value -= step_x @@ -163,29 +163,29 @@ class Silhouette(BaseEstimator, TransformerMixin): """ This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. """ - def __init__(self, weight=lambda x: 1, resolution=100, sh_range=[np.nan, np.nan]): + def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): """ Constructor for the Silhouette class. Attributes: weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. resolution (int): number of samples for the weighted average (default 100). - sh_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ - self.weight, self.resolution, self.sh_range = weight, resolution, sh_range + self.weight, self.resolution, self.sample_range = weight, resolution, sample_range def fit(self, X, y=None): """ - Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sh_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ - if np.isnan(np.array(self.sh_range)).any(): - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ - self.sh_range = np.where(np.isnan(np.array(self.sh_range)), np.array([mx, My]), np.array(self.sh_range)) + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) return self def transform(self, X): @@ -199,7 +199,7 @@ class Silhouette(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sh_range[0], self.sh_range[1], self.resolution) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) step_x = x_values[1] - x_values[0] for i in range(num_diag): @@ -213,20 +213,20 @@ class Silhouette(BaseEstimator, TransformerMixin): for j in range(num_pts_in_diag): - [px,py] = diagram[j,:] + [px,py] = diagram[j,:2] weight = weights[j] / total_weight - min_idx = np.minimum(np.maximum(np.ceil((px - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sh_range[0]) / step_x).astype(int), 0), self.resolution) + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) if min_idx < self.resolution and max_idx > 0: - silhouette_value = self.sh_range[0] + min_idx * step_x - px + silhouette_value = self.sample_range[0] + min_idx * step_x - px for k in range(min_idx, mid_idx): sh[k] += weight * silhouette_value silhouette_value += step_x - silhouette_value = py - self.sh_range[0] - mid_idx * step_x + silhouette_value = py - self.sample_range[0] - mid_idx * step_x for k in range(mid_idx, max_idx): sh[k] += weight * silhouette_value silhouette_value -= step_x @@ -241,28 +241,28 @@ class BettiCurve(BaseEstimator, TransformerMixin): """ This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. """ - def __init__(self, resolution=100, bc_range=[np.nan, np.nan]): + def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): """ Constructor for the BettiCurve class. Attributes: resolution (int): number of sample for the piecewise-constant function (default 100). - bc_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ - self.resolution, self.bc_range = resolution, bc_range + self.resolution, self.sample_range = resolution, sample_range def fit(self, X, y=None): """ - Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **bc_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ - if np.isnan(np.array(self.bc_range)).any(): - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ - self.bc_range = np.where(np.isnan(np.array(self.bc_range)), np.array([mx, My]), np.array(self.bc_range)) + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) return self def transform(self, X): @@ -276,7 +276,7 @@ class BettiCurve(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.bc_range[0], self.bc_range[1], self.resolution) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) step_x = x_values[1] - x_values[0] for i in range(num_diag): @@ -285,9 +285,9 @@ class BettiCurve(BaseEstimator, TransformerMixin): bc = np.zeros(self.resolution) for j in range(num_pts_in_diag): - [px,py] = diagram[j,:] - min_idx = np.minimum(np.maximum(np.ceil((px - self.bc_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.bc_range[0]) / step_x).astype(int), 0), self.resolution) + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) for k in range(min_idx, max_idx): bc[k] += 1 @@ -301,7 +301,7 @@ class Entropy(BaseEstimator, TransformerMixin): """ This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. """ - def __init__(self, mode="scalar", normalized=True, resolution=100, ent_range=[np.nan, np.nan]): + def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): """ Constructor for the Entropy class. @@ -309,9 +309,9 @@ class Entropy(BaseEstimator, TransformerMixin): mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". - ent_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". + sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". """ - self.mode, self.normalized, self.resolution, self.ent_range = mode, normalized, resolution, ent_range + self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range def fit(self, X, y=None): """ @@ -321,10 +321,10 @@ class Entropy(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ - if np.isnan(np.array(self.ent_range)).any(): - pre = DiagramPreprocessor(use=True, scalers=[([0,1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = pre.scalers[0][1].data_min_, pre.scalers[0][1].data_max_ - self.ent_range = np.where(np.isnan(np.array(self.ent_range)), np.array([mx, My]), np.array(self.ent_range)) + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) return self def transform(self, X): @@ -338,14 +338,14 @@ class Entropy(BaseEstimator, TransformerMixin): Xfit (numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**)): output entropy. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.ent_range[0], self.ent_range[1], self.resolution) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) step_x = x_values[1] - x_values[0] - new_X = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(X) + new_X = BirthPersistenceTransform().fit_transform(X) for i in range(num_diag): orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] - new_diagram = DiagramPreprocessor(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] + new_diagram = DiagramScaler(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] if self.mode == "scalar": ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) @@ -354,9 +354,9 @@ class Entropy(BaseEstimator, TransformerMixin): else: ent = np.zeros(self.resolution) for j in range(num_pts_in_diag): - [px,py] = orig_diagram[j,:] - min_idx = np.minimum(np.maximum(np.ceil((px - self.ent_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.ent_range[0]) / step_x).astype(int), 0), self.resolution) + [px,py] = orig_diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) for k in range(min_idx, max_idx): ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) if self.normalized: @@ -411,7 +411,7 @@ class TopologicalVector(BaseEstimator, TransformerMixin): for i in range(num_diag): diagram, num_pts_in_diag = X[i], X[i].shape[0] - pers = 0.5 * np.matmul(diagram, np.array([[-1.0],[1.0]])) + pers = 0.5 * (diagram[:,1]-diagram[:,0]) min_pers = np.minimum(pers,np.transpose(pers)) distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) -- cgit v1.2.3 From e83f7bbc33170ea04a9c2b4db1767ab51bfb4a56 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 10 Sep 2019 16:40:15 +0200 Subject: Move cython directory stuff to python directory. Make example work. Fix CMake for sktda. Make sktda a submodule of gudhi package. --- src/cython/example/ex_diagrams.py | 107 ------- src/cython/sktda/kernel_methods.py | 202 ------------- src/cython/sktda/metrics.py | 242 ---------------- src/cython/sktda/preprocessing.py | 298 ------------------- src/cython/sktda/vector_methods.py | 481 ------------------------------- src/python/CMakeLists.txt | 2 + src/python/example/ex_diagrams.py | 106 +++++++ src/python/gudhi/sktda/__init__.py | 6 + src/python/gudhi/sktda/kernel_methods.py | 202 +++++++++++++ src/python/gudhi/sktda/metrics.py | 239 +++++++++++++++ src/python/gudhi/sktda/preprocessing.py | 298 +++++++++++++++++++ src/python/gudhi/sktda/vector_methods.py | 481 +++++++++++++++++++++++++++++++ 12 files changed, 1334 insertions(+), 1330 deletions(-) delete mode 100644 src/cython/example/ex_diagrams.py delete mode 100644 src/cython/sktda/kernel_methods.py delete mode 100644 src/cython/sktda/metrics.py delete mode 100644 src/cython/sktda/preprocessing.py delete mode 100644 src/cython/sktda/vector_methods.py create mode 100644 src/python/example/ex_diagrams.py create mode 100644 src/python/gudhi/sktda/__init__.py create mode 100644 src/python/gudhi/sktda/kernel_methods.py create mode 100644 src/python/gudhi/sktda/metrics.py create mode 100644 src/python/gudhi/sktda/preprocessing.py create mode 100644 src/python/gudhi/sktda/vector_methods.py diff --git a/src/cython/example/ex_diagrams.py b/src/cython/example/ex_diagrams.py deleted file mode 100644 index aee20245..00000000 --- a/src/cython/example/ex_diagrams.py +++ /dev/null @@ -1,107 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np -from sklearn.kernel_approximation import RBFSampler - -import sys -sys.path.append("../sktda/") -from preprocessing import * -from metrics import * -from vector_methods import * -from kernel_methods import * - -D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.]]) -plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,10.],[0.,10.]) -plt.show() - -diags = [D] - -LS = Landscape(resolution = 1000) -L = LS.fit_transform(diags) -plt.plot(L[0][:1000]) -plt.plot(L[0][1000:2000]) -plt.plot(L[0][2000:3000]) -plt.show() - -def pow(n): - return lambda x: np.power(x[1]-x[0],n) - -SH = Silhouette(resolution=1000, weight=pow(2)) -sh = SH.fit_transform(diags) -plt.plot(sh[0]) -plt.show() - -BC = BettiCurve(resolution=1000) -bc = BC.fit_transform(diags) -plt.plot(bc[0]) -plt.show() - -CP = ComplexPolynomial(threshold=-1, F="T") -cp = CP.fit_transform(diags) -print("Complex polynomial is " + str(cp[0,:])) - -TV = TopologicalVector(threshold=-1) -tv = TV.fit_transform(diags) -print("Topological vector is " + str(tv[0,:])) - -diagsT = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(diags) -PI = PersistenceImage(bandwidth=1., weight=lambda x: x[1], im_range=[0,10,0,10], resolution=[100,100]) -pi = PI.fit_transform(diagsT) -plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) -plt.show() - -plt.scatter(D[:,0],D[:,1]) -D = np.array([[1.,5.],[3.,6.],[2.,7.]]) -plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,10.],[0.,10.]) -plt.show() - -diags2 = [D] - -def arctan(C,p): - return lambda x: C*np.arctan(np.power(x[1], p)) - -PWG = PersistenceWeightedGaussianKernel(bandwidth=1., kernel_approx=None, weight=arctan(1.,1.)) -X = PWG.fit(diags) -Y = PWG.transform(diags2) -print("PWG kernel is " + str(Y[0][0])) - -PWG = PersistenceWeightedGaussianKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2])), weight=arctan(1.,1.)) -X = PWG.fit(diags) -Y = PWG.transform(diags2) -print("Approximate PWG kernel is " + str(Y[0][0])) - -PSS = PersistenceScaleSpaceKernel(bandwidth=1.) -X = PSS.fit(diags) -Y = PSS.transform(diags2) -print("PSS kernel is " + str(Y[0][0])) - -PSS = PersistenceScaleSpaceKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) -X = PSS.fit(diags) -Y = PSS.transform(diags2) -print("Approximate PSS kernel is " + str(Y[0][0])) - -sW = SlicedWassersteinDistance(num_directions=100) -X = sW.fit(diags) -Y = sW.transform(diags2) -print("SW distance is " + str(Y[0][0])) - -SW = SlicedWassersteinKernel(num_directions=100, bandwidth=1.) -X = SW.fit(diags) -Y = SW.transform(diags2) -print("SW kernel is " + str(Y[0][0])) - -W = BottleneckDistance(epsilon=.001) -X = W.fit(diags) -Y = W.transform(diags2) -print("Bottleneck distance is " + str(Y[0][0])) - -PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1.) -X = PF.fit(diags) -Y = PF.transform(diags2) -print("PF kernel is " + str(Y[0][0])) - -PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1., kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) -X = PF.fit(diags) -Y = PF.transform(diags2) -print("Approximate PF kernel is " + str(Y[0][0])) diff --git a/src/cython/sktda/kernel_methods.py b/src/cython/sktda/kernel_methods.py deleted file mode 100644 index d90bf164..00000000 --- a/src/cython/sktda/kernel_methods.py +++ /dev/null @@ -1,202 +0,0 @@ -""" -@author: Mathieu Carriere -All rights reserved -""" - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.metrics import pairwise_distances -from .metrics import SlicedWassersteinDistance, PersistenceFisherDistance - -############################################# -# Kernel methods ############################ -############################################# - -class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the sliced Wasserstein kernel matrix from a list of persistence diagrams. The sliced Wasserstein kernel is computed by exponentiating the corresponding sliced Wasserstein distance with a Gaussian kernel. See http://proceedings.mlr.press/v70/carriere17a.html for more details. - """ - def __init__(self, num_directions=10, bandwidth=1.0): - """ - Constructor for the SlicedWassersteinDistance class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. - """ - self.bandwidth = bandwidth - self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) - - def fit(self, X, y=None): - """ - Fit the SlicedWassersteinKernel class on a list of persistence diagrams: an instance of the SlicedWassersteinDistance class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.sw_.fit(X, y) - return self - - def transform(self, X): - """ - Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein kernel values. - """ - return np.exp(-self.sw_.transform(X)/self.bandwidth) - -class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence weighted Gaussian kernel matrix from a list of persistence diagrams. The persistence weighted Gaussian kernel is computed by convolving the persistence diagram points with weighted Gaussian kernels. See http://proceedings.mlr.press/v48/kusano16.html for more details. - """ - def __init__(self, bandwidth=1., weight=lambda x: 1, kernel_approx=None): - """ - Constructor for the PersistenceWeightedGaussianKernel class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth, self.weight = bandwidth, weight - self.kernel_approx = kernel_approx - - def fit(self, X, y=None): - """ - Fit the PersistenceWeightedGaussianKernel class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = list(X) - self.ws_ = [ np.array([self.weight(self.diagrams_[i][j,:]) for j in range(self.diagrams_[i].shape[0])]) for i in range(len(self.diagrams_)) ] - if self.kernel_approx is not None: - self.approx_ = np.concatenate([np.sum(np.multiply(self.ws_[i][:,np.newaxis], self.kernel_approx.transform(self.diagrams_[i])), axis=0)[np.newaxis,:] for i in range(len(self.diagrams_))]) - return self - - def transform(self, X): - """ - Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence weighted Gaussian kernel values. - """ - Xp = list(X) - Xfit = np.zeros((len(Xp), len(self.diagrams_))) - if len(self.diagrams_) == len(Xp) and np.all([np.array_equal(self.diagrams_[i], Xp[i]) for i in range(len(Xp))]): - if self.kernel_approx is not None: - Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(self.approx_, self.approx_.T) - else: - for i in range(len(self.diagrams_)): - for j in range(i+1, len(self.diagrams_)): - W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) - E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) - Xfit[i,j] = np.sum(np.multiply(W, E)) - Xfit[j,i] = Xfit[i,j] - else: - ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] - if self.kernel_approx is not None: - approx = np.concatenate([np.sum(np.multiply(ws[i][:,np.newaxis], self.kernel_approx.transform(Xp[i])), axis=0)[np.newaxis,:] for i in range(len(Xp))]) - Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(approx, self.approx_.T) - else: - for i in range(len(Xp)): - for j in range(len(self.diagrams_)): - W = np.matmul(ws[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) - E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(Xp[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) - Xfit[i,j] = np.sum(np.multiply(W, E)) - - return Xfit - -class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. - """ - def __init__(self, bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceScaleSpaceKernel class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.pwg_ = PersistenceWeightedGaussianKernel(bandwidth=bandwidth, weight=lambda x: 1 if x[1] >= x[0] else -1, kernel_approx=kernel_approx) - - def fit(self, X, y=None): - """ - Fit the PersistenceScaleSpaceKernel class on a list of persistence diagrams: symmetric to the diagonal of all points are computed and an instance of the PersistenceWeightedGaussianKernel class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = list(X) - for i in range(len(self.diagrams_)): - op_D = np.matmul(self.diagrams_[i], np.array([[0.,1.], [1.,0.]])) - self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) - self.pwg_.fit(X) - return self - - def transform(self, X): - """ - Compute all persistence scale space kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence scale space kernel values. - """ - Xp = list(X) - for i in range(len(Xp)): - op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) - Xp[i] = np.concatenate([Xp[i], op_X], axis=0) - return self.pwg_.transform(Xp) - -class PersistenceFisherKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence Fisher kernel matrix from a list of persistence diagrams. The persistence Fisher kernel is computed by exponentiating the corresponding persistence Fisher distance with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. - """ - def __init__(self, bandwidth_fisher=1., bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceFisherKernel class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). - bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth = bandwidth - self.pf_ = PersistenceFisherDistance(bandwidth=bandwidth_fisher, kernel_approx=kernel_approx) - - def fit(self, X, y=None): - """ - Fit the PersistenceFisherKernel class on a list of persistence diagrams: an instance of the PersistenceFisherDistance class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.pf_.fit(X, y) - return self - - def transform(self, X): - """ - Compute all persistence Fisher kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher kernel values. - """ - return np.exp(-self.pf_.transform(X)/self.bandwidth) - diff --git a/src/cython/sktda/metrics.py b/src/cython/sktda/metrics.py deleted file mode 100644 index 18db432a..00000000 --- a/src/cython/sktda/metrics.py +++ /dev/null @@ -1,242 +0,0 @@ -""" -@author: Mathieu Carriere -All rights reserved -""" - -import sys -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.metrics import pairwise_distances -try: - from gudhi import bottleneck_distance - USE_GUDHI = True -except ImportError: - USE_GUDHI = False - print("Gudhi not found: BottleneckDistance will return null matrix, and exact SlicedWassersteinDistance not available") - -############################################# -# Metrics ################################### -############################################# - -class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the sliced Wasserstein distance matrix from a list of persistence diagrams. The Sliced Wasserstein distance is computed by projecting the persistence diagrams onto lines, comparing the projections with the 1-norm, and finally integrating over all possible lines. See http://proceedings.mlr.press/v70/carriere17a.html for more details. - """ - def __init__(self, num_directions=10): - """ - Constructor for the SlicedWassersteinDistance class. - - Attributes: - num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the distance computation (default 10). - """ - self.num_directions = num_directions - thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] - self.lines_ = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=0) - - def fit(self, X, y=None): - """ - Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in a numpy array called **diagrams**. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - self.approx_ = [np.matmul(X[i], self.lines_) for i in range(len(X))] - diag_proj = (1./2) * np.ones((2,2)) - self.approx_diag_ = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] - return self - - def transform(self, X): - """ - Compute all sliced Wasserstein distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein distances. - """ - Xfit = np.zeros((len(X), len(self.approx_))) - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - for i in range(len(self.approx_)): - for j in range(i+1, len(self.approx_)): - A = np.sort(np.concatenate([self.approx_[i], self.approx_diag_[j]], axis=0), axis=0) - B = np.sort(np.concatenate([self.approx_[j], self.approx_diag_[i]], axis=0), axis=0) - L1 = np.sum(np.abs(A-B), axis=0) - Xfit[i,j] = np.mean(L1) - Xfit[j,i] = Xfit[i,j] - else: - diag_proj = (1./2) * np.ones((2,2)) - approx = [np.matmul(X[i], self.lines_) for i in range(len(X))] - approx_diag = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] - for i in range(len(approx)): - for j in range(len(self.approx_)): - A = np.sort(np.concatenate([approx[i], self.approx_diag_[j]], axis=0), axis=0) - B = np.sort(np.concatenate([self.approx_[j], approx_diag[i]], axis=0), axis=0) - L1 = np.sum(np.abs(A-B), axis=0) - Xfit[i,j] = np.mean(L1) - - return Xfit - -class BottleneckDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. - """ - def __init__(self, epsilon=1e-3): - """ - Constructor for the BottleneckDistance class. - - Attributes: - epsilon (double): approximation quality (default 1e-4). - """ - self.epsilon = epsilon - - def fit(self, X, y=None): - """ - Fit the BottleneckDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams**. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - return self - - def transform(self, X): - """ - Compute all bottleneck distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise bottleneck distances. - """ - num_diag1 = len(X) - - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - matrix = np.zeros((num_diag1, num_diag1)) - - if USE_GUDHI: - for i in range(num_diag1): - #sys.stdout.write( str(i*1.0 / num_diag1) + "\r") - for j in range(i+1, num_diag1): - matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) - matrix[j,i] = matrix[i,j] - else: - print("Gudhi required---returning null matrix") - - else: - num_diag2 = len(self.diagrams_) - matrix = np.zeros((num_diag1, num_diag2)) - - if USE_GUDHI: - for i in range(num_diag1): - #sys.stdout.write( str(i*1.0 / num_diag1) + "\r") - for j in range(num_diag2): - matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) - else: - print("Gudhi required---returning null matrix") - - Xfit = matrix - - return Xfit - -class PersistenceFisherDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See http://papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. - """ - def __init__(self, bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceFisherDistance class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth, self.kernel_approx = bandwidth, kernel_approx - - def fit(self, X, y=None): - """ - Fit the PersistenceFisherDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - projection = (1./2) * np.ones((2,2)) - self.diagonal_projections_ = [np.matmul(X[i], projection) for i in range(len(X))] - if self.kernel_approx is not None: - self.approx_ = [self.kernel_approx.transform(X[i]) for i in range(len(X))] - self.approx_diagonal_ = [self.kernel_approx.transform(self.diagonal_projections_[i]) for i in range(len(X))] - return self - - def transform(self, X): - """ - Compute all persistence Fisher distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher distances. - """ - Xfit = np.zeros((len(X), len(self.diagrams_))) - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - for i in range(len(self.diagrams_)): - for j in range(i+1, len(self.diagrams_)): - if self.kernel_approx is not None: - Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) - U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) - vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - Xfit[j,i] = Xfit[i,j] - else: - Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) - U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) - vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - Xfit[j,i] = Xfit[i,j] - else: - projection = (1./2) * np.ones((2,2)) - diagonal_projections = [np.matmul(X[i], projection) for i in range(len(X))] - if self.kernel_approx is not None: - approx = [self.kernel_approx.transform(X[i]) for i in range(len(X))] - approx_diagonal = [self.kernel_approx.transform(diagonal_projections[i]) for i in range(len(X))] - for i in range(len(X)): - for j in range(len(self.diagrams_)): - if self.kernel_approx is not None: - Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) - U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) - vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - else: - Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) - U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) - vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - return Xfit diff --git a/src/cython/sktda/preprocessing.py b/src/cython/sktda/preprocessing.py deleted file mode 100644 index 512b02f3..00000000 --- a/src/cython/sktda/preprocessing.py +++ /dev/null @@ -1,298 +0,0 @@ -""" -@author: Mathieu Carriere -All rights reserved -""" - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.preprocessing import StandardScaler - -############################################# -# Preprocessing ############################# -############################################# - -class BirthPersistenceTransform(BaseEstimator, TransformerMixin): - """ - This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. - """ - def __init__(self): - """ - Constructor for BirthPersistenceTransform class. - """ - return None - - def fit(self, X, y=None): - """ - Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). - - Parameters: - X (n x 2 numpy array): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Apply the BirthPersistenceTransform function on the persistence diagrams. - - Parameters: - X (list of n x 2 numpy array): input persistence diagrams. - - Returns: - Xfit (list of n x 2 numpy array): transformed persistence diagrams. - """ - Xfit = [] - for diag in X: - new_diag = np.empty(diag.shape) - np.copyto(new_diag, diag) - new_diag[:,1] = new_diag[:,1] - new_diag[:,0] - Xfit.append(new_diag) - return Xfit - -class Clamping(BaseEstimator, TransformerMixin): - """ - This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. - """ - def __init__(self, limit=np.inf): - """ - Constructor for the Clamping class. - - Attributes: - limit (double): clamping value (default np.inf). - """ - self.limit = limit - - def fit(self, X, y=None): - """ - Fit the Clamping class on a list of list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). - - Parameters: - X (list of numpy arrays of size n): input values. - y (n x 1 array): value labels (unused). - """ - return self - - def transform(self, X): - """ - Clamp each list of values individually. - - Parameters: - X (list of numpy arrays of size n): input list of list of values. - - Returns: - Xfit (list of numpy arrays of size n): output list of list of values. - """ - Xfit = [np.where(L >= self.limit, self.limit * np.ones(L.shape), L) for L in X] - return Xfit - -class DiagramScaler(BaseEstimator, TransformerMixin): - """ - This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. - """ - def __init__(self, use=False, scalers=[]): - """ - Constructor for the DiagramPreprocessor class. - - Attributes: - use (bool): whether to use the class or not (default False). - scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). - """ - self.scalers = scalers - self.use = use - - def fit(self, X, y=None): - """ - Fit the DiagramPreprocessor class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if self.use: - if len(X) == 1: - P = X[0] - else: - P = np.concatenate(X,0) - for (indices, scaler) in self.scalers: - scaler.fit(np.reshape(P[:,indices], [-1, 1])) - return self - - def transform(self, X): - """ - Apply the DiagramPreprocessor function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): transformed persistence diagrams. - """ - Xfit = [np.copy(d) for d in X] - if self.use: - for i in range(len(Xfit)): - if Xfit[i].shape[0] > 0: - for (indices, scaler) in self.scalers: - Xfit[i][:,indices] = scaler.transform(Xfit[i][:,indices]) - return Xfit - -class Padding(BaseEstimator, TransformerMixin): - """ - This is a class for padding a list of persistence diagrams with dummy points, so that all persistence diagrams end up with the same number of points. - """ - def __init__(self, use=False): - """ - Constructor for the Padding class. - - Attributes: - use (bool): whether to use the class or not (default False). - """ - self.use = use - - def fit(self, X, y=None): - """ - Fit the Padding class on a list of persistence diagrams (this function actually does nothing but is useful when Padding is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.max_pts = max([len(diag) for diag in X]) - return self - - def transform(self, X): - """ - Add dummy points to each persistence diagram so that they all have the same cardinality. All points are given an additional coordinate indicating if the point was added after padding (0) or already present before (1). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - Xfit (list of n x 3 or n x 2 numpy arrays): padded persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - for diag in X: - diag_pad = np.pad(diag, ((0,max(0, self.max_pts - diag.shape[0])), (0,1)), "constant", constant_values=((0,0),(0,0))) - diag_pad[:diag.shape[0],2] = np.ones(diag.shape[0]) - Xfit.append(diag_pad) - else: - Xfit = X - return Xfit - -class ProminentPoints(BaseEstimator, TransformerMixin): - """ - This is a class for removing points that are close or far from the diagonal in persistence diagrams. If persistence diagrams are n x 2 numpy arrays (i.e. persistence diagrams with ordinary features), points are ordered and thresholded by distance-to-diagonal. If persistence diagrams are n x 1 numpy arrays (i.e. persistence diagrams with essential features), points are not ordered and thresholded by first coordinate. - """ - def __init__(self, use=False, num_pts=10, threshold=-1, location="upper"): - """ - Constructor for the ProminentPoints class. - - Attributes: - use (bool): whether to use the class or not (default False). - location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. - num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. - threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. - """ - self.num_pts = num_pts - self.threshold = threshold - self.use = use - self.location = location - - def fit(self, X, y=None): - """ - Fit the ProminentPoints class on a list of persistence diagrams (this function actually does nothing but is useful when ProminentPoints is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): thresholded persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - for i in range(num_diag): - diag = X[i] - if diag.shape[1] >= 2: - if diag.shape[0] > 0: - pers = np.abs(diag[:,1] - diag[:,0]) - idx_thresh = pers >= self.threshold - thresh_diag, thresh_pers = diag[idx_thresh], pers[idx_thresh] - sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) - if self.location == "upper": - new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] - if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh] ], axis=0) - else: - new_diag = diag - - else: - if diag.shape[0] > 0: - birth = diag[:,:1] - idx_thresh = birth >= self.threshold - thresh_diag, thresh_birth = diag[idx_thresh], birth[idx_thresh] - if self.location == "upper": - new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] - if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh] ], axis=0) - else: - new_diag = diag - - Xfit.append(new_diag) - else: - Xfit = X - return Xfit - -class DiagramSelector(BaseEstimator, TransformerMixin): - """ - This is a class for extracting finite or essential points in persistence diagrams. - """ - def __init__(self, use=False, limit=np.inf, point_type="finite"): - """ - Constructor for the DiagramSelector class. - - Attributes: - use (bool): whether to use the class or not (default False). - limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). - point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. - """ - self.use, self.limit, self.point_type = use, limit, point_type - - def fit(self, X, y=None): - """ - Fit the DiagramSelector class on a list of persistence diagrams (this function actually does nothing but is useful when DiagramSelector is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Extract and return the finite or essential points of each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): extracted persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - if self.point_type == "finite": - Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] - else: - Xfit = [ diag[diag[:,1] == self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] - else: - Xfit = X - return Xfit diff --git a/src/cython/sktda/vector_methods.py b/src/cython/sktda/vector_methods.py deleted file mode 100644 index 3862f815..00000000 --- a/src/cython/sktda/vector_methods.py +++ /dev/null @@ -1,481 +0,0 @@ -""" -@author: Mathieu Carriere -All rights reserved -""" - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler -from sklearn.neighbors import DistanceMetric - -from .preprocessing import DiagramScaler, BirthPersistenceTransform - -############################################# -# Finite Vectorization methods ############## -############################################# - -class PersistenceImage(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence images from a list of persistence diagrams. A persistence image is a 2D function computed from a persistence diagram by convolving the diagram points with a weighted Gaussian kernel. The plane is then discretized into an image with pixels, which is flattened and returned as a vector. See http://jmlr.org/papers/v18/16-337.html for more details. - """ - def __init__(self, bandwidth=1., weight=lambda x: 1, resolution=[20,20], im_range=[np.nan, np.nan, np.nan, np.nan]): - """ - Constructor for the PersistenceImage class. - - Attributes: - bandwidth (double): bandwidth of the Gaussian kernel (default 1.). - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. - resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). - im_range ([double,double,double,double]): minimum and maximum of each axis of the persistence image, of the form [x_min, x_max, y_min, y_max] (default [numpy.nan, numpy.nan, numpy.nan, numpyp.nan]). If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.bandwidth, self.weight = bandwidth, weight - self.resolution, self.im_range = resolution, im_range - - def fit(self, X, y=None): - """ - Fit the PersistenceImage class on a list of persistence diagrams: if any of the values in **im_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.im_range)).any(): - new_X = BirthPersistenceTransform().fit_transform(X) - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(new_X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) - return self - - def transform(self, X): - """ - Compute the persistence image for each persistence diagram individually and store the results in a single numpy array. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. - """ - num_diag, Xfit = len(X), [] - new_X = BirthPersistenceTransform().fit_transform(X) - - for i in range(num_diag): - - diagram, num_pts_in_diag = new_X[i], X[i].shape[0] - - w = np.empty(num_pts_in_diag) - for j in range(num_pts_in_diag): - w[j] = self.weight(diagram[j,:]) - - x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) - Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) - image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(self.bandwidth*np.sqrt(2*np.pi)), 1) - - Xfit.append(image.flatten()[np.newaxis,:]) - - Xfit = np.concatenate(Xfit,0) - - return Xfit - -class Landscape(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. - """ - def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Landscape class. - - Attributes: - num_landscapes (int): number of piecewise-linear functions to output (default 5). - resolution (int): number of sample for all piecewise-linear functions (default 100). - sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Landscape class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the persistence landscape for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**)): output persistence landscapes. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - ls = np.zeros([self.num_landscapes, self.resolution]) - - events = [] - for j in range(self.resolution): - events.append([]) - - for j in range(num_pts_in_diag): - [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - - if min_idx < self.resolution and max_idx > 0: - - landscape_value = self.sample_range[0] + min_idx * step_x - px - for k in range(min_idx, mid_idx): - events[k].append(landscape_value) - landscape_value += step_x - - landscape_value = py - self.sample_range[0] - mid_idx * step_x - for k in range(mid_idx, max_idx): - events[k].append(landscape_value) - landscape_value -= step_x - - for j in range(self.resolution): - events[j].sort(reverse=True) - for k in range( min(self.num_landscapes, len(events[j])) ): - ls[k,j] = events[j][k] - - Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) - - Xfit = np.concatenate(Xfit,0) - - return Xfit - -class Silhouette(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. - """ - def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Silhouette class. - - Attributes: - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. - resolution (int): number of samples for the weighted average (default 100). - sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.weight, self.resolution, self.sample_range = weight, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the persistence silhouette for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - sh, weights = np.zeros(self.resolution), np.zeros(num_pts_in_diag) - for j in range(num_pts_in_diag): - weights[j] = self.weight(diagram[j,:]) - total_weight = np.sum(weights) - - for j in range(num_pts_in_diag): - - [px,py] = diagram[j,:2] - weight = weights[j] / total_weight - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - - if min_idx < self.resolution and max_idx > 0: - - silhouette_value = self.sample_range[0] + min_idx * step_x - px - for k in range(min_idx, mid_idx): - sh[k] += weight * silhouette_value - silhouette_value += step_x - - silhouette_value = py - self.sample_range[0] - mid_idx * step_x - for k in range(mid_idx, max_idx): - sh[k] += weight * silhouette_value - silhouette_value -= step_x - - Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class BettiCurve(BaseEstimator, TransformerMixin): - """ - This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. - """ - def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the BettiCurve class. - - Attributes: - resolution (int): number of sample for the piecewise-constant function (default 100). - sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.resolution, self.sample_range = resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the Betti curve for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - bc = np.zeros(self.resolution) - for j in range(num_pts_in_diag): - [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - for k in range(min_idx, max_idx): - bc[k] += 1 - - Xfit.append(np.reshape(bc,[1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class Entropy(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. - """ - def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Entropy class. - - Attributes: - mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). - normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". - resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". - sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". - """ - self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Entropy class on a list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the entropy for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**)): output entropy. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - new_X = BirthPersistenceTransform().fit_transform(X) - - for i in range(num_diag): - - orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] - new_diagram = DiagramScaler(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] - - if self.mode == "scalar": - ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) - Xfit.append(np.array([[ent]])) - - else: - ent = np.zeros(self.resolution) - for j in range(num_pts_in_diag): - [px,py] = orig_diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - for k in range(min_idx, max_idx): - ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) - if self.normalized: - ent = ent / np.linalg.norm(ent, ord=1) - Xfit.append(np.reshape(ent,[1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class TopologicalVector(BaseEstimator, TransformerMixin): - """ - This is a class for computing topological vectors from a list of persistence diagrams. The topological vector associated to a persistence diagram is the sorted vector of a slight modification of the pairwise distances between the persistence diagram points. See https://diglib.eg.org/handle/10.1111/cgf12692 for more details. - """ - def __init__(self, threshold=10): - """ - Constructor for the TopologicalVector class. - - Attributes: - threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. - """ - self.threshold = threshold - - def fit(self, X, y=None): - """ - Fit the TopologicalVector class on a list of persistence diagrams (this function actually does nothing but is useful when TopologicalVector is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Compute the topological vector for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. - """ - if self.threshold == -1: - thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() - else: - thresh = self.threshold - - num_diag = len(X) - Xfit = np.zeros([num_diag, thresh]) - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - pers = 0.5 * (diagram[:,1]-diagram[:,0]) - min_pers = np.minimum(pers,np.transpose(pers)) - distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) - vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) - dim = min(len(vect), thresh) - Xfit[i, :dim] = vect[:dim] - - return Xfit - -class ComplexPolynomial(BaseEstimator, TransformerMixin): - """ - This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. - """ - def __init__(self, F="R", threshold=10): - """ - Constructor for the ComplexPolynomial class. - - Attributes: - F (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). - threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. - """ - self.threshold, self.F = threshold, F - - def fit(self, X, y=None): - """ - Fit the ComplexPolynomial class on a list of persistence diagrams (this function actually does nothing but is useful when ComplexPolynomial is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Compute the complex vector of coefficients for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - Xfit (numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. - """ - if self.threshold == -1: - thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() - else: - thresh = self.threshold - - Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) - for d in range(len(X)): - D, N = X[d], X[d].shape[0] - if self.F == "R": - roots = D[:,0] + 1j * D[:,1] - elif self.F == "S": - alpha = np.linalg.norm(D, axis=1) - alpha = np.where(alpha==0, np.ones(N), alpha) - roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) - elif self.F == "T": - alpha = np.linalg.norm(D, axis=1) - roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) - coeff = [0] * (N+1) - coeff[N] = 1 - for i in range(1, N+1): - for j in range(N-i-1, N): - coeff[j] += ((-1) * roots[i-1] * coeff[j+1]) - coeff = np.array(coeff[::-1])[1:] - Xfit[d, :min(thresh, coeff.shape[0])] = coeff[:min(thresh, coeff.shape[0])] - return Xfit diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 9e128d30..26cd6ec1 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -49,6 +49,7 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'alpha_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ") + set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'sktda', ") add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") add_gudhi_debug_info("Cython version ${CYTHON_VERSION}") @@ -199,6 +200,7 @@ if(PYTHONINTERP_FOUND) # Other .py files file(COPY "gudhi/persistence_graphical_tools.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") + file(COPY "gudhi/sktda" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/") add_custom_command( OUTPUT gudhi.so diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py new file mode 100644 index 00000000..3dc6fe84 --- /dev/null +++ b/src/python/example/ex_diagrams.py @@ -0,0 +1,106 @@ +import matplotlib.pyplot as plt +import numpy as np +from sklearn.kernel_approximation import RBFSampler + +from gudhi.sktda import Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ + TopologicalVector, DiagramScaler, BirthPersistenceTransform,\ + PersistenceImage, PersistenceWeightedGaussianKernel,\ + PersistenceScaleSpaceKernel, SlicedWassersteinDistance,\ + SlicedWassersteinKernel, BottleneckDistance, PersistenceFisherKernel + +D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.]]) +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,10.],[0.,10.]) +plt.show() + +diags = [D] + +LS = Landscape(resolution = 1000) +L = LS.fit_transform(diags) +plt.plot(L[0][:1000]) +plt.plot(L[0][1000:2000]) +plt.plot(L[0][2000:3000]) +plt.show() + +def pow(n): + return lambda x: np.power(x[1]-x[0],n) + +SH = Silhouette(resolution=1000, weight=pow(2)) +sh = SH.fit_transform(diags) +plt.plot(sh[0]) +plt.show() + +BC = BettiCurve(resolution=1000) +bc = BC.fit_transform(diags) +plt.plot(bc[0]) +plt.show() + +CP = ComplexPolynomial(threshold=-1, F="T") +cp = CP.fit_transform(diags) +print("Complex polynomial is " + str(cp[0,:])) + +TV = TopologicalVector(threshold=-1) +tv = TV.fit_transform(diags) +print("Topological vector is " + str(tv[0,:])) + +#diagsT = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(diags) +#PI = PersistenceImage(bandwidth=1., weight=lambda x: x[1], im_range=[0,10,0,10], resolution=[100,100]) +#pi = PI.fit_transform(diagsT) +#plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) +#plt.show() + +plt.scatter(D[:,0],D[:,1]) +D = np.array([[1.,5.],[3.,6.],[2.,7.]]) +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,10.],[0.,10.]) +plt.show() + +diags2 = [D] + +def arctan(C,p): + return lambda x: C*np.arctan(np.power(x[1], p)) + +PWG = PersistenceWeightedGaussianKernel(bandwidth=1., kernel_approx=None, weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("PWG kernel is " + str(Y[0][0])) + +PWG = PersistenceWeightedGaussianKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2])), weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("Approximate PWG kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(bandwidth=1.) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("PSS kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("Approximate PSS kernel is " + str(Y[0][0])) + +sW = SlicedWassersteinDistance(num_directions=100) +X = sW.fit(diags) +Y = sW.transform(diags2) +print("SW distance is " + str(Y[0][0])) + +SW = SlicedWassersteinKernel(num_directions=100, bandwidth=1.) +X = SW.fit(diags) +Y = SW.transform(diags2) +print("SW kernel is " + str(Y[0][0])) + +W = BottleneckDistance(epsilon=.001) +X = W.fit(diags) +Y = W.transform(diags2) +print("Bottleneck distance is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1.) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("PF kernel is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1., kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("Approximate PF kernel is " + str(Y[0][0])) diff --git a/src/python/gudhi/sktda/__init__.py b/src/python/gudhi/sktda/__init__.py new file mode 100644 index 00000000..f020248d --- /dev/null +++ b/src/python/gudhi/sktda/__init__.py @@ -0,0 +1,6 @@ +from .kernel_methods import * +from .metrics import * +from .preprocessing import * +from .vector_methods import * + +__all__ = ["kernel_methods", "metrics", "preprocessing", "vector_methods"] diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py new file mode 100644 index 00000000..d90bf164 --- /dev/null +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -0,0 +1,202 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +from .metrics import SlicedWassersteinDistance, PersistenceFisherDistance + +############################################# +# Kernel methods ############################ +############################################# + +class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein kernel matrix from a list of persistence diagrams. The sliced Wasserstein kernel is computed by exponentiating the corresponding sliced Wasserstein distance with a Gaussian kernel. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10, bandwidth=1.0): + """ + Constructor for the SlicedWassersteinDistance class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). + num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + """ + self.bandwidth = bandwidth + self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinKernel class on a list of persistence diagrams: an instance of the SlicedWassersteinDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.sw_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein kernel values. + """ + return np.exp(-self.sw_.transform(X)/self.bandwidth) + +class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence weighted Gaussian kernel matrix from a list of persistence diagrams. The persistence weighted Gaussian kernel is computed by convolving the persistence diagram points with weighted Gaussian kernels. See http://proceedings.mlr.press/v48/kusano16.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, kernel_approx=None): + """ + Constructor for the PersistenceWeightedGaussianKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.weight = bandwidth, weight + self.kernel_approx = kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceWeightedGaussianKernel class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + self.ws_ = [ np.array([self.weight(self.diagrams_[i][j,:]) for j in range(self.diagrams_[i].shape[0])]) for i in range(len(self.diagrams_)) ] + if self.kernel_approx is not None: + self.approx_ = np.concatenate([np.sum(np.multiply(self.ws_[i][:,np.newaxis], self.kernel_approx.transform(self.diagrams_[i])), axis=0)[np.newaxis,:] for i in range(len(self.diagrams_))]) + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence weighted Gaussian kernel values. + """ + Xp = list(X) + Xfit = np.zeros((len(Xp), len(self.diagrams_))) + if len(self.diagrams_) == len(Xp) and np.all([np.array_equal(self.diagrams_[i], Xp[i]) for i in range(len(Xp))]): + if self.kernel_approx is not None: + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(self.approx_, self.approx_.T) + else: + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + Xfit[j,i] = Xfit[i,j] + else: + ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] + if self.kernel_approx is not None: + approx = np.concatenate([np.sum(np.multiply(ws[i][:,np.newaxis], self.kernel_approx.transform(Xp[i])), axis=0)[np.newaxis,:] for i in range(len(Xp))]) + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(approx, self.approx_.T) + else: + for i in range(len(Xp)): + for j in range(len(self.diagrams_)): + W = np.matmul(ws[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(Xp[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + + return Xfit + +class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceScaleSpaceKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.pwg_ = PersistenceWeightedGaussianKernel(bandwidth=bandwidth, weight=lambda x: 1 if x[1] >= x[0] else -1, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceScaleSpaceKernel class on a list of persistence diagrams: symmetric to the diagonal of all points are computed and an instance of the PersistenceWeightedGaussianKernel class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + for i in range(len(self.diagrams_)): + op_D = np.matmul(self.diagrams_[i], np.array([[0.,1.], [1.,0.]])) + self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) + self.pwg_.fit(X) + return self + + def transform(self, X): + """ + Compute all persistence scale space kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence scale space kernel values. + """ + Xp = list(X) + for i in range(len(Xp)): + op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) + Xp[i] = np.concatenate([Xp[i], op_X], axis=0) + return self.pwg_.transform(Xp) + +class PersistenceFisherKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher kernel matrix from a list of persistence diagrams. The persistence Fisher kernel is computed by exponentiating the corresponding persistence Fisher distance with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth_fisher=1., bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherKernel class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). + bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth = bandwidth + self.pf_ = PersistenceFisherDistance(bandwidth=bandwidth_fisher, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherKernel class on a list of persistence diagrams: an instance of the PersistenceFisherDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.pf_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all persistence Fisher kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher kernel values. + """ + return np.exp(-self.pf_.transform(X)/self.bandwidth) + diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py new file mode 100644 index 00000000..8092f7af --- /dev/null +++ b/src/python/gudhi/sktda/metrics.py @@ -0,0 +1,239 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +try: + from .. import bottleneck_distance + USE_GUDHI = True +except ImportError: + USE_GUDHI = False + print("Gudhi not found: BottleneckDistance will return null matrix, and exact SlicedWassersteinDistance not available") + +############################################# +# Metrics ################################### +############################################# + +class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein distance matrix from a list of persistence diagrams. The Sliced Wasserstein distance is computed by projecting the persistence diagrams onto lines, comparing the projections with the 1-norm, and finally integrating over all possible lines. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10): + """ + Constructor for the SlicedWassersteinDistance class. + + Attributes: + num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the distance computation (default 10). + """ + self.num_directions = num_directions + thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] + self.lines_ = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=0) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in a numpy array called **diagrams**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + self.approx_ = [np.matmul(X[i], self.lines_) for i in range(len(X))] + diag_proj = (1./2) * np.ones((2,2)) + self.approx_diag_ = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein distances. + """ + Xfit = np.zeros((len(X), len(self.approx_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.approx_)): + for j in range(i+1, len(self.approx_)): + A = np.sort(np.concatenate([self.approx_[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], self.approx_diag_[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + Xfit[j,i] = Xfit[i,j] + else: + diag_proj = (1./2) * np.ones((2,2)) + approx = [np.matmul(X[i], self.lines_) for i in range(len(X))] + approx_diag = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + for i in range(len(approx)): + for j in range(len(self.approx_)): + A = np.sort(np.concatenate([approx[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], approx_diag[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + + return Xfit + +class BottleneckDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. + """ + def __init__(self, epsilon=1e-3): + """ + Constructor for the BottleneckDistance class. + + Attributes: + epsilon (double): approximation quality (default 1e-4). + """ + self.epsilon = epsilon + + def fit(self, X, y=None): + """ + Fit the BottleneckDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + return self + + def transform(self, X): + """ + Compute all bottleneck distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise bottleneck distances. + """ + num_diag1 = len(X) + + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + matrix = np.zeros((num_diag1, num_diag1)) + + if USE_GUDHI: + for i in range(num_diag1): + for j in range(i+1, num_diag1): + matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) + matrix[j,i] = matrix[i,j] + else: + print("Gudhi required---returning null matrix") + + else: + num_diag2 = len(self.diagrams_) + matrix = np.zeros((num_diag1, num_diag2)) + + if USE_GUDHI: + for i in range(num_diag1): + for j in range(num_diag2): + matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) + else: + print("Gudhi required---returning null matrix") + + Xfit = matrix + + return Xfit + +class PersistenceFisherDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See http://papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherDistance class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.kernel_approx = bandwidth, kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + projection = (1./2) * np.ones((2,2)) + self.diagonal_projections_ = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + self.approx_ = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + self.approx_diagonal_ = [self.kernel_approx.transform(self.diagonal_projections_[i]) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all persistence Fisher distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher distances. + """ + Xfit = np.zeros((len(X), len(self.diagrams_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + Xfit[j,i] = Xfit[i,j] + else: + Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + Xfit[j,i] = Xfit[i,j] + else: + projection = (1./2) * np.ones((2,2)) + diagonal_projections = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + approx = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + approx_diagonal = [self.kernel_approx.transform(diagonal_projections[i]) for i in range(len(X))] + for i in range(len(X)): + for j in range(len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + else: + Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + return Xfit diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py new file mode 100644 index 00000000..512b02f3 --- /dev/null +++ b/src/python/gudhi/sktda/preprocessing.py @@ -0,0 +1,298 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import StandardScaler + +############################################# +# Preprocessing ############################# +############################################# + +class BirthPersistenceTransform(BaseEstimator, TransformerMixin): + """ + This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. + """ + def __init__(self): + """ + Constructor for BirthPersistenceTransform class. + """ + return None + + def fit(self, X, y=None): + """ + Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). + + Parameters: + X (n x 2 numpy array): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Apply the BirthPersistenceTransform function on the persistence diagrams. + + Parameters: + X (list of n x 2 numpy array): input persistence diagrams. + + Returns: + Xfit (list of n x 2 numpy array): transformed persistence diagrams. + """ + Xfit = [] + for diag in X: + new_diag = np.empty(diag.shape) + np.copyto(new_diag, diag) + new_diag[:,1] = new_diag[:,1] - new_diag[:,0] + Xfit.append(new_diag) + return Xfit + +class Clamping(BaseEstimator, TransformerMixin): + """ + This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. + """ + def __init__(self, limit=np.inf): + """ + Constructor for the Clamping class. + + Attributes: + limit (double): clamping value (default np.inf). + """ + self.limit = limit + + def fit(self, X, y=None): + """ + Fit the Clamping class on a list of list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). + + Parameters: + X (list of numpy arrays of size n): input values. + y (n x 1 array): value labels (unused). + """ + return self + + def transform(self, X): + """ + Clamp each list of values individually. + + Parameters: + X (list of numpy arrays of size n): input list of list of values. + + Returns: + Xfit (list of numpy arrays of size n): output list of list of values. + """ + Xfit = [np.where(L >= self.limit, self.limit * np.ones(L.shape), L) for L in X] + return Xfit + +class DiagramScaler(BaseEstimator, TransformerMixin): + """ + This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. + """ + def __init__(self, use=False, scalers=[]): + """ + Constructor for the DiagramPreprocessor class. + + Attributes: + use (bool): whether to use the class or not (default False). + scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). + """ + self.scalers = scalers + self.use = use + + def fit(self, X, y=None): + """ + Fit the DiagramPreprocessor class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if self.use: + if len(X) == 1: + P = X[0] + else: + P = np.concatenate(X,0) + for (indices, scaler) in self.scalers: + scaler.fit(np.reshape(P[:,indices], [-1, 1])) + return self + + def transform(self, X): + """ + Apply the DiagramPreprocessor function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): transformed persistence diagrams. + """ + Xfit = [np.copy(d) for d in X] + if self.use: + for i in range(len(Xfit)): + if Xfit[i].shape[0] > 0: + for (indices, scaler) in self.scalers: + Xfit[i][:,indices] = scaler.transform(Xfit[i][:,indices]) + return Xfit + +class Padding(BaseEstimator, TransformerMixin): + """ + This is a class for padding a list of persistence diagrams with dummy points, so that all persistence diagrams end up with the same number of points. + """ + def __init__(self, use=False): + """ + Constructor for the Padding class. + + Attributes: + use (bool): whether to use the class or not (default False). + """ + self.use = use + + def fit(self, X, y=None): + """ + Fit the Padding class on a list of persistence diagrams (this function actually does nothing but is useful when Padding is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.max_pts = max([len(diag) for diag in X]) + return self + + def transform(self, X): + """ + Add dummy points to each persistence diagram so that they all have the same cardinality. All points are given an additional coordinate indicating if the point was added after padding (0) or already present before (1). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 3 or n x 2 numpy arrays): padded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + for diag in X: + diag_pad = np.pad(diag, ((0,max(0, self.max_pts - diag.shape[0])), (0,1)), "constant", constant_values=((0,0),(0,0))) + diag_pad[:diag.shape[0],2] = np.ones(diag.shape[0]) + Xfit.append(diag_pad) + else: + Xfit = X + return Xfit + +class ProminentPoints(BaseEstimator, TransformerMixin): + """ + This is a class for removing points that are close or far from the diagonal in persistence diagrams. If persistence diagrams are n x 2 numpy arrays (i.e. persistence diagrams with ordinary features), points are ordered and thresholded by distance-to-diagonal. If persistence diagrams are n x 1 numpy arrays (i.e. persistence diagrams with essential features), points are not ordered and thresholded by first coordinate. + """ + def __init__(self, use=False, num_pts=10, threshold=-1, location="upper"): + """ + Constructor for the ProminentPoints class. + + Attributes: + use (bool): whether to use the class or not (default False). + location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. + num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. + threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. + """ + self.num_pts = num_pts + self.threshold = threshold + self.use = use + self.location = location + + def fit(self, X, y=None): + """ + Fit the ProminentPoints class on a list of persistence diagrams (this function actually does nothing but is useful when ProminentPoints is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): thresholded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + for i in range(num_diag): + diag = X[i] + if diag.shape[1] >= 2: + if diag.shape[0] > 0: + pers = np.abs(diag[:,1] - diag[:,0]) + idx_thresh = pers >= self.threshold + thresh_diag, thresh_pers = diag[idx_thresh], pers[idx_thresh] + sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) + if self.location == "upper": + new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh] ], axis=0) + else: + new_diag = diag + + else: + if diag.shape[0] > 0: + birth = diag[:,:1] + idx_thresh = birth >= self.threshold + thresh_diag, thresh_birth = diag[idx_thresh], birth[idx_thresh] + if self.location == "upper": + new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh] ], axis=0) + else: + new_diag = diag + + Xfit.append(new_diag) + else: + Xfit = X + return Xfit + +class DiagramSelector(BaseEstimator, TransformerMixin): + """ + This is a class for extracting finite or essential points in persistence diagrams. + """ + def __init__(self, use=False, limit=np.inf, point_type="finite"): + """ + Constructor for the DiagramSelector class. + + Attributes: + use (bool): whether to use the class or not (default False). + limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). + point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. + """ + self.use, self.limit, self.point_type = use, limit, point_type + + def fit(self, X, y=None): + """ + Fit the DiagramSelector class on a list of persistence diagrams (this function actually does nothing but is useful when DiagramSelector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Extract and return the finite or essential points of each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + Xfit (list of n x 2 or n x 1 numpy arrays): extracted persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + if self.point_type == "finite": + Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] + else: + Xfit = [ diag[diag[:,1] == self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] + else: + Xfit = X + return Xfit diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py new file mode 100644 index 00000000..3862f815 --- /dev/null +++ b/src/python/gudhi/sktda/vector_methods.py @@ -0,0 +1,481 @@ +""" +@author: Mathieu Carriere +All rights reserved +""" + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler +from sklearn.neighbors import DistanceMetric + +from .preprocessing import DiagramScaler, BirthPersistenceTransform + +############################################# +# Finite Vectorization methods ############## +############################################# + +class PersistenceImage(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence images from a list of persistence diagrams. A persistence image is a 2D function computed from a persistence diagram by convolving the diagram points with a weighted Gaussian kernel. The plane is then discretized into an image with pixels, which is flattened and returned as a vector. See http://jmlr.org/papers/v18/16-337.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, resolution=[20,20], im_range=[np.nan, np.nan, np.nan, np.nan]): + """ + Constructor for the PersistenceImage class. + + Attributes: + bandwidth (double): bandwidth of the Gaussian kernel (default 1.). + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). + im_range ([double,double,double,double]): minimum and maximum of each axis of the persistence image, of the form [x_min, x_max, y_min, y_max] (default [numpy.nan, numpy.nan, numpy.nan, numpyp.nan]). If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.bandwidth, self.weight = bandwidth, weight + self.resolution, self.im_range = resolution, im_range + + def fit(self, X, y=None): + """ + Fit the PersistenceImage class on a list of persistence diagrams: if any of the values in **im_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.im_range)).any(): + new_X = BirthPersistenceTransform().fit_transform(X) + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(new_X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) + return self + + def transform(self, X): + """ + Compute the persistence image for each persistence diagram individually and store the results in a single numpy array. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. + """ + num_diag, Xfit = len(X), [] + new_X = BirthPersistenceTransform().fit_transform(X) + + for i in range(num_diag): + + diagram, num_pts_in_diag = new_X[i], X[i].shape[0] + + w = np.empty(num_pts_in_diag) + for j in range(num_pts_in_diag): + w[j] = self.weight(diagram[j,:]) + + x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) + Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) + image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(self.bandwidth*np.sqrt(2*np.pi)), 1) + + Xfit.append(image.flatten()[np.newaxis,:]) + + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Landscape(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. + """ + def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Landscape class. + + Attributes: + num_landscapes (int): number of piecewise-linear functions to output (default 5). + resolution (int): number of sample for all piecewise-linear functions (default 100). + sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Landscape class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the persistence landscape for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**)): output persistence landscapes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + ls = np.zeros([self.num_landscapes, self.resolution]) + + events = [] + for j in range(self.resolution): + events.append([]) + + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + landscape_value = self.sample_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + events[k].append(landscape_value) + landscape_value += step_x + + landscape_value = py - self.sample_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + events[k].append(landscape_value) + landscape_value -= step_x + + for j in range(self.resolution): + events[j].sort(reverse=True) + for k in range( min(self.num_landscapes, len(events[j])) ): + ls[k,j] = events[j][k] + + Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) + + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Silhouette(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. + """ + def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Silhouette class. + + Attributes: + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. + resolution (int): number of samples for the weighted average (default 100). + sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.weight, self.resolution, self.sample_range = weight, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the persistence silhouette for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + sh, weights = np.zeros(self.resolution), np.zeros(num_pts_in_diag) + for j in range(num_pts_in_diag): + weights[j] = self.weight(diagram[j,:]) + total_weight = np.sum(weights) + + for j in range(num_pts_in_diag): + + [px,py] = diagram[j,:2] + weight = weights[j] / total_weight + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + silhouette_value = self.sample_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + sh[k] += weight * silhouette_value + silhouette_value += step_x + + silhouette_value = py - self.sample_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + sh[k] += weight * silhouette_value + silhouette_value -= step_x + + Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class BettiCurve(BaseEstimator, TransformerMixin): + """ + This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. + """ + def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the BettiCurve class. + + Attributes: + resolution (int): number of sample for the piecewise-constant function (default 100). + sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.resolution, self.sample_range = resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the Betti curve for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + bc = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + bc[k] += 1 + + Xfit.append(np.reshape(bc,[1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class Entropy(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. + """ + def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Entropy class. + + Attributes: + mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). + normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". + resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". + sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". + """ + self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Entropy class on a list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the entropy for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**)): output entropy. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + new_X = BirthPersistenceTransform().fit_transform(X) + + for i in range(num_diag): + + orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] + new_diagram = DiagramScaler(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] + + if self.mode == "scalar": + ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) + Xfit.append(np.array([[ent]])) + + else: + ent = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = orig_diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) + if self.normalized: + ent = ent / np.linalg.norm(ent, ord=1) + Xfit.append(np.reshape(ent,[1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class TopologicalVector(BaseEstimator, TransformerMixin): + """ + This is a class for computing topological vectors from a list of persistence diagrams. The topological vector associated to a persistence diagram is the sorted vector of a slight modification of the pairwise distances between the persistence diagram points. See https://diglib.eg.org/handle/10.1111/cgf12692 for more details. + """ + def __init__(self, threshold=10): + """ + Constructor for the TopologicalVector class. + + Attributes: + threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. + """ + self.threshold = threshold + + def fit(self, X, y=None): + """ + Fit the TopologicalVector class on a list of persistence diagrams (this function actually does nothing but is useful when TopologicalVector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the topological vector for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + num_diag = len(X) + Xfit = np.zeros([num_diag, thresh]) + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + pers = 0.5 * (diagram[:,1]-diagram[:,0]) + min_pers = np.minimum(pers,np.transpose(pers)) + distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) + vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) + dim = min(len(vect), thresh) + Xfit[i, :dim] = vect[:dim] + + return Xfit + +class ComplexPolynomial(BaseEstimator, TransformerMixin): + """ + This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. + """ + def __init__(self, F="R", threshold=10): + """ + Constructor for the ComplexPolynomial class. + + Attributes: + F (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). + threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. + """ + self.threshold, self.F = threshold, F + + def fit(self, X, y=None): + """ + Fit the ComplexPolynomial class on a list of persistence diagrams (this function actually does nothing but is useful when ComplexPolynomial is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the complex vector of coefficients for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + Xfit (numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) + for d in range(len(X)): + D, N = X[d], X[d].shape[0] + if self.F == "R": + roots = D[:,0] + 1j * D[:,1] + elif self.F == "S": + alpha = np.linalg.norm(D, axis=1) + alpha = np.where(alpha==0, np.ones(N), alpha) + roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) + elif self.F == "T": + alpha = np.linalg.norm(D, axis=1) + roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) + coeff = [0] * (N+1) + coeff[N] = 1 + for i in range(1, N+1): + for j in range(N-i-1, N): + coeff[j] += ((-1) * roots[i-1] * coeff[j+1]) + coeff = np.array(coeff[::-1])[1:] + Xfit[d, :min(thresh, coeff.shape[0])] = coeff[:min(thresh, coeff.shape[0])] + return Xfit -- cgit v1.2.3 From af98fb120eea4ebc09531de9f74684b50212ab7a Mon Sep 17 00:00:00 2001 From: mathieu Date: Wed, 11 Sep 2019 20:16:02 -0400 Subject: fixed error in DiagramScaler --- src/python/gudhi/sktda/preprocessing.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 512b02f3..3c625053 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -64,25 +64,25 @@ class Clamping(BaseEstimator, TransformerMixin): def fit(self, X, y=None): """ - Fit the Clamping class on a list of list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). + Fit the Clamping class on a list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). Parameters: - X (list of numpy arrays of size n): input values. + X (numpy array of size n): input values. y (n x 1 array): value labels (unused). """ return self def transform(self, X): """ - Clamp each list of values individually. + Clamp list of values. Parameters: - X (list of numpy arrays of size n): input list of list of values. + X (numpy array of size n): input list of values. Returns: - Xfit (list of numpy arrays of size n): output list of list of values. + Xfit (numpy array of size n): output list of values. """ - Xfit = [np.where(L >= self.limit, self.limit * np.ones(L.shape), L) for L in X] + Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) return Xfit class DiagramScaler(BaseEstimator, TransformerMixin): @@ -132,7 +132,8 @@ class DiagramScaler(BaseEstimator, TransformerMixin): for i in range(len(Xfit)): if Xfit[i].shape[0] > 0: for (indices, scaler) in self.scalers: - Xfit[i][:,indices] = scaler.transform(Xfit[i][:,indices]) + for I in indices: + Xfit[i][:,I] = np.squeeze(scaler.transform(np.reshape(Xfit[i][:,I], [-1,1]))) return Xfit class Padding(BaseEstimator, TransformerMixin): -- cgit v1.2.3 From 1b007fc59f08bd01e1521eb1c0773b598bdf158b Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 11:14:24 +0200 Subject: wasserstein distance added on fork --- src/python/doc/wasserstein_distance_sum.inc | 14 ++++++ src/python/doc/wasserstein_distance_user.rst | 39 +++++++++++++++ src/python/gudhi/wasserstein.py | 75 ++++++++++++++++++++++++++++ src/python/test/test_wasserstein_distance.py | 22 ++++++++ 4 files changed, 150 insertions(+) create mode 100644 src/python/doc/wasserstein_distance_sum.inc create mode 100644 src/python/doc/wasserstein_distance_user.rst create mode 100644 src/python/gudhi/wasserstein.py create mode 100755 src/python/test/test_wasserstein_distance.py diff --git a/src/python/doc/wasserstein_distance_sum.inc b/src/python/doc/wasserstein_distance_sum.inc new file mode 100644 index 00000000..0263f80f --- /dev/null +++ b/src/python/doc/wasserstein_distance_sum.inc @@ -0,0 +1,14 @@ +.. table:: + :widths: 30 50 20 + + +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ + | .. figure:: | The p-Wasserstein distance measures the similarity between two | :Author: Theo Lacombe | + | ../../doc/Bottleneck_distance/perturb_pd.png | persistence diagrams. It's the minimum value c that can be achieve by| | + | :figclass: align-center | a perfect matching between the points of the two diagrams (+ all the | :Introduced in: GUDHI 2.0.0 | + | | diagonal points), where the value of a matching is defined as the | | + | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edges lengths to the power p. Edges | :Copyright: MIT (`GPL v3 `_) | + | edges lengths to the power p. | lengths are measured in norm q, for $1 \leq q \leq \infty$. | | + | | | :Requires: `Python Optimal Transport (POT)` | + +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ + | * :doc:`wasserstein_distance_user` | | + +-----------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/src/python/doc/wasserstein_distance_user.rst b/src/python/doc/wasserstein_distance_user.rst new file mode 100644 index 00000000..a51cfb71 --- /dev/null +++ b/src/python/doc/wasserstein_distance_user.rst @@ -0,0 +1,39 @@ +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +Wasserstein distance user manual +=============================== +Definition +---------- + +.. include:: wasserstein_distance_sum.inc + +This implementation is based on ideas from "Large Scale Computation of Means and Cluster for Persistence Diagrams via Optimal Transport". + +Function +-------- +.. autofunction:: gudhi.wasserstein_distance + + +Basic example +------------- + +This example computes the 1-Wasserstein distance from 2 persistence diagrams with euclidean ground metric. +Note that persistence diagrams must be submitted as (n x 2) numpy arrays and must not contain inf values. + +.. testcode:: + + import gudhi + + diag1 = np.array([[2.7, 3.7],[9.6, 14.],[34.2, 34.974]]) + diag2 = np.array([[2.8, 4.45],[9.5, 14.1]]) + + message = "Wasserstein distance value = " + '%.2f' % gudhi.wasserstein_distance(diag1, diag2, q=2., p=1.) + print(message) + +The output is: + +.. testoutput:: + + Wasserstein distance value = 1.45 diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py new file mode 100644 index 00000000..cc527ed8 --- /dev/null +++ b/src/python/gudhi/wasserstein.py @@ -0,0 +1,75 @@ +import numpy as np +import scipy.spatial.distance as sc +try: + import ot +except ImportError: + print("POT (Python Optimal Transport) package is not installed. Try to run $ pip install POT") + +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Theo Lacombe + + Copyright (C) 2016 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification +""" + +def proj_on_diag(X): + ''' + param X: (n x 2) array encoding the points of a persistent diagram. + return: (n x 2) arary encoding the (respective orthogonal) projections of the points onto the diagonal + ''' + Z = (X[:,0] + X[:,1]) / 2. + return np.array([Z , Z]).T + + +def build_dist_matrix(X, Y, p=2., q=2.): + ''' + param X: (n x 2) np.array encoding the (points of the) first diagram. + param Y: (m x 2) np.array encoding the second diagram. + param q: Ground metric (i.e. norm l_q). + param p: exponent for the Wasserstein metric. + return: (n+1) x (m+1) np.array encoding the cost matrix C. + For 1 <= i <= n, 1 <= j <= m, C[i,j] encodes the distance between X[i] and Y[j], while C[i, m+1] (resp. C[n+1, j]) encodes the distance (to the p) between X[i] (resp Y[j]) and its orthogonal proj onto the diagonal. + note also that C[n+1, m+1] = 0 (it costs nothing to move from the diagonal to the diagonal). + ''' + Xdiag = proj_on_diag(X) + Ydiag = proj_on_diag(Y) + if np.isinf(p): + C = sc.cdist(X,Y, metric='chebyshev', p=q)**p + Cxd = np.linalg.norm(X - Xdiag, ord=q, axis=1)**p + Cdy = np.linalg.norm(Y - Ydiag, ord=q, axis=1)**p + else: + C = sc.cdist(X,Y, metric='minkowski', p=q)**p + Cxd = np.linalg.norm(X - Xdiag, ord=q, axis=1)**p + Cdy = np.linalg.norm(Y - Ydiag, ord=q, axis=1)**p + Cf = np.hstack((C, Cxd[:,None])) + Cdy = np.append(Cdy, 0) + + Cf = np.vstack((Cf, Cdy[None,:])) + + return Cf + + +def wasserstein_distance(X, Y, p=2., q=2.): + ''' + param X, Y: (n x 2) and (m x 2) numpy array (points of persistence diagrams) + param q: Ground metric (i.e. norm l_q); Default value is 2 (euclidean norm). + param p: exponent for Wasserstein; Default value is 2. + return: float, the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. + ''' + M = build_dist_matrix(X, Y, p=p, q=q) + n = len(X) + m = len(Y) + a = 1.0 / (n + m) * np.ones(n) # weight vector of the input diagram. Uniform here. + hat_a = np.append(a, m/(n+m)) # so that we have a probability measure, required by POT + b = 1.0 / (n + m) * np.ones(m) # weight vector of the input diagram. Uniform here. + hat_b = np.append(b, n/(m+n)) # so that we have a probability measure, required by POT + + # Comptuation of the otcost using the ot.emd2 library. + # Note: it is the squared Wasserstein distance. + ot_cost = (n+m) * ot.emd2(hat_a, hat_b, M) + + return np.power(ot_cost, 1./p) + diff --git a/src/python/test/test_wasserstein_distance.py b/src/python/test/test_wasserstein_distance.py new file mode 100755 index 00000000..a5f7cf77 --- /dev/null +++ b/src/python/test/test_wasserstein_distance.py @@ -0,0 +1,22 @@ +import gudhi + +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Theo Lacombe + + Copyright (C) 2016 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification +""" + +__author__ = "Theo Lacombe" +__copyright__ = "Copyright (C) 2016 Inria" +__license__ = "MIT" + + +def test_basic_bottleneck(): + diag1 = np.array([[2.7, 3.7], [9.6, 14.0], [34.2, 34.974]]) + diag2 = np.array([[2.8, 4.45], [9.5, 14.1]]) + + assert gudhi.wasserstein_distance(diag1, diag2) == 1.4453593023967701 -- cgit v1.2.3 From 52d095ab15b6c308218861608975cc8ed90d187e Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:07:22 +0200 Subject: update of CMakeLists and third_party_libraries.cmake to add wasserstein distance and POT dependancies --- src/cmake/modules/GUDHI_third_party_libraries.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index 360a230b..5c76eeae 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -125,6 +125,7 @@ if( PYTHONINTERP_FOUND ) find_python_module("numpy") find_python_module("scipy") find_python_module("sphinx") + find_python_module("ot") endif() if(NOT GUDHI_PYTHON_PATH) -- cgit v1.2.3 From 982fa3738f847b53c72e43c3c854ff47ce846d1c Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:08:01 +0200 Subject: update CMakeLists --- src/python/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 9e128d30..063a19e8 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -49,6 +49,7 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'alpha_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ") + set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'wasserstein', ") add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") add_gudhi_debug_info("Cython version ${CYTHON_VERSION}") @@ -199,6 +200,7 @@ if(PYTHONINTERP_FOUND) # Other .py files file(COPY "gudhi/persistence_graphical_tools.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") + file(COPY "gudhi/wasserstein.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") add_custom_command( OUTPUT gudhi.so @@ -371,6 +373,11 @@ if(PYTHONINTERP_FOUND) # Reader utils add_gudhi_py_test(test_reader_utils) + # Wasserstein + if(OT_FOUND) + add_gudhi_py_test(test_wasserstein_distance) + endif(OT_FOUND) + # Documentation generation is available through sphinx - requires all modules if(SPHINX_PATH) if(MATPLOTLIB_FOUND) -- cgit v1.2.3 From cb3346903c8b37ca617bf8c01e00eedc03031624 Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:08:45 +0200 Subject: update test wasserstein. --- src/python/test/test_wasserstein_distance.py | 36 ++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/python/test/test_wasserstein_distance.py b/src/python/test/test_wasserstein_distance.py index a5f7cf77..c1b568e2 100755 --- a/src/python/test/test_wasserstein_distance.py +++ b/src/python/test/test_wasserstein_distance.py @@ -1,22 +1,50 @@ import gudhi +import numpy as np """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Theo Lacombe - Copyright (C) 2016 Inria + Copyright (C) 2019 Inria Modification(s): - YYYY/MM Author: Description of the modification """ __author__ = "Theo Lacombe" -__copyright__ = "Copyright (C) 2016 Inria" +__copyright__ = "Copyright (C) 2019 Inria" __license__ = "MIT" -def test_basic_bottleneck(): +def test_basic_wasserstein(): diag1 = np.array([[2.7, 3.7], [9.6, 14.0], [34.2, 34.974]]) diag2 = np.array([[2.8, 4.45], [9.5, 14.1]]) + diag3 = np.array([[0, 2], [4, 6]]) + diag4 = np.array([[0, 3], [4, 8]]) + emptydiag = np.array([[]]) + + assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=2., p=1.) == 0. + assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=1.) == 0. + assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=2.) == 0. + assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=2., p=2.) == 0. + + assert gudhi.wasserstein_distance(diag3, emptydiag, q=np.inf, p=1.) == 2. + assert gudhi.wasserstein_distance(diag3, emptydiag, q=1., p=1.) == 4. + + assert gudhi.wasserstein_distance(diag4, emptydiag, q=1., p=2.) == 5. # thank you Pythagorician triplets + assert gudhi.wasserstein_distance(diag4, emptydiag, q=np.inf, p=2.) == 2.5 + assert gudhi.wasserstein_distance(diag4, emptydiag, q=2., p=2.) == 3.5355339059327378 + + assert gudhi.wasserstein_distance(diag1, diag2, q=2., p=1.) == 1.4453593023967701 + assert gudhi.wasserstein_distance(diag1, diag2, q=2.35, p=1.74) == 0.9772734057168739 + + assert gudhi.wasserstein_distance(diag1, emptydiag, q=2.35, p=1.7863) == 3.141592214572228 + + assert gudhi.wasserstein_distance(diag3, diag4, q=1., p=1.) == 3. + assert gudhi.wasserstein_distance(diag3, diag4, q=np.inf, p=1.) == 3. # no diag matching here + assert gudhi.wasserstein_distance(diag3, diag4, q=np.inf, p=2.) == np.sqrt(5) + assert gudhi.wasserstein_distance(diag3, diag4, q=1., p=2.) == np.sqrt(5) + assert gudhi.wasserstein_distance(diag3, diag4, q=4.5, p=2.) == np.sqrt(5) + + - assert gudhi.wasserstein_distance(diag1, diag2) == 1.4453593023967701 -- cgit v1.2.3 From 36dfb09493f56f666367df39e5d1a170e49a1a23 Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:10:14 +0200 Subject: updated doc for sphinx compilation --- src/python/doc/wasserstein_distance_user.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/python/doc/wasserstein_distance_user.rst b/src/python/doc/wasserstein_distance_user.rst index a51cfb71..bcb7f19d 100644 --- a/src/python/doc/wasserstein_distance_user.rst +++ b/src/python/doc/wasserstein_distance_user.rst @@ -25,6 +25,7 @@ Note that persistence diagrams must be submitted as (n x 2) numpy arrays and mus .. testcode:: import gudhi + import numpy as np diag1 = np.array([[2.7, 3.7],[9.6, 14.],[34.2, 34.974]]) diag2 = np.array([[2.8, 4.45],[9.5, 14.1]]) -- cgit v1.2.3 From 3c98951fd157fe750f7df5b29258a19d4d314c1e Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:11:34 +0200 Subject: updated wasserstein.py ; added _ in front of private functions, added q=np.inf, added emptydiagram management. --- src/python/gudhi/wasserstein.py | 51 +++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index cc527ed8..db42cc08 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -9,13 +9,13 @@ except ImportError: See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Theo Lacombe - Copyright (C) 2016 Inria + Copyright (C) 2019 Inria Modification(s): - YYYY/MM Author: Description of the modification """ -def proj_on_diag(X): +def _proj_on_diag(X): ''' param X: (n x 2) array encoding the points of a persistent diagram. return: (n x 2) arary encoding the (respective orthogonal) projections of the points onto the diagonal @@ -24,7 +24,7 @@ def proj_on_diag(X): return np.array([Z , Z]).T -def build_dist_matrix(X, Y, p=2., q=2.): +def _build_dist_matrix(X, Y, p=2., q=2.): ''' param X: (n x 2) np.array encoding the (points of the) first diagram. param Y: (m x 2) np.array encoding the second diagram. @@ -34,10 +34,10 @@ def build_dist_matrix(X, Y, p=2., q=2.): For 1 <= i <= n, 1 <= j <= m, C[i,j] encodes the distance between X[i] and Y[j], while C[i, m+1] (resp. C[n+1, j]) encodes the distance (to the p) between X[i] (resp Y[j]) and its orthogonal proj onto the diagonal. note also that C[n+1, m+1] = 0 (it costs nothing to move from the diagonal to the diagonal). ''' - Xdiag = proj_on_diag(X) - Ydiag = proj_on_diag(Y) - if np.isinf(p): - C = sc.cdist(X,Y, metric='chebyshev', p=q)**p + Xdiag = _proj_on_diag(X) + Ydiag = _proj_on_diag(Y) + if np.isinf(q): + C = sc.cdist(X,Y, metric='chebyshev')**p Cxd = np.linalg.norm(X - Xdiag, ord=q, axis=1)**p Cdy = np.linalg.norm(Y - Ydiag, ord=q, axis=1)**p else: @@ -52,24 +52,45 @@ def build_dist_matrix(X, Y, p=2., q=2.): return Cf +def _perstot(X, p, q): + ''' + param X: (n x 2) numpy array (points of a given diagram) + param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). + param p: exponent for Wasserstein; Default value is 2. + return: float, the total persistence of the diagram (that is, its distance to the empty diagram). + ''' + Xdiag = _proj_on_diag(X) + return (np.sum(np.linalg.norm(X - Xdiag, ord=q, axis=1)**p))**(1/p) + + def wasserstein_distance(X, Y, p=2., q=2.): ''' param X, Y: (n x 2) and (m x 2) numpy array (points of persistence diagrams) - param q: Ground metric (i.e. norm l_q); Default value is 2 (euclidean norm). + param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). param p: exponent for Wasserstein; Default value is 2. return: float, the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. ''' - M = build_dist_matrix(X, Y, p=p, q=q) n = len(X) m = len(Y) - a = 1.0 / (n + m) * np.ones(n) # weight vector of the input diagram. Uniform here. - hat_a = np.append(a, m/(n+m)) # so that we have a probability measure, required by POT - b = 1.0 / (n + m) * np.ones(m) # weight vector of the input diagram. Uniform here. - hat_b = np.append(b, n/(m+n)) # so that we have a probability measure, required by POT + + # handle empty diagrams + if X.size == 0: + if Y.size == 0: + return 0. + else: + return _perstot(Y, p, q) + elif Y.size == 0: + return _perstot(X, p, q) + + M = _build_dist_matrix(X, Y, p=p, q=q) + a = np.full(n+1, 1. / (n + m) ) # weight vector of the input diagram. Uniform here. + a[-1] = a[-1] * m # normalized so that we have a probability measure, required by POT + b = np.full(m+1, 1. / (n + m) ) # weight vector of the input diagram. Uniform here. + b[-1] = b[-1] * n # so that we have a probability measure, required by POT # Comptuation of the otcost using the ot.emd2 library. # Note: it is the squared Wasserstein distance. - ot_cost = (n+m) * ot.emd2(hat_a, hat_b, M) + ot_cost = (n+m) * ot.emd2(a, b, M) - return np.power(ot_cost, 1./p) + return ot_cost ** (1./p) -- cgit v1.2.3 From b0c4bcce51e4c17660b378c374796d6d300002ed Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 23 Sep 2019 18:16:27 +0200 Subject: updated pot install instructions, including conda --- src/python/gudhi/wasserstein.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index db42cc08..ae5f75a5 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -3,7 +3,7 @@ import scipy.spatial.distance as sc try: import ot except ImportError: - print("POT (Python Optimal Transport) package is not installed. Try to run $ pip install POT") + print("POT (Python Optimal Transport) package is not installed. Try to run $ conda install -c conda-forge pot ; or $ pip install POT") """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -- cgit v1.2.3 From 2268124c398fdfb83547bec4f9d704dffdd0f673 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 24 Sep 2019 13:28:26 +0200 Subject: Rename Dockerfile for circleci. Add a Dockerfile to test the installation --- Dockerfile_for_circleci_image | 58 ++++++++++++++++++++++++++++++++++++++ Dockerfile_gudhi_installation | 65 +++++++++++++++++++++++++++++++++++++++++++ Dockerfile_ubuntu | 58 -------------------------------------- 3 files changed, 123 insertions(+), 58 deletions(-) create mode 100644 Dockerfile_for_circleci_image create mode 100644 Dockerfile_gudhi_installation delete mode 100644 Dockerfile_ubuntu diff --git a/Dockerfile_for_circleci_image b/Dockerfile_for_circleci_image new file mode 100644 index 00000000..e149a33a --- /dev/null +++ b/Dockerfile_for_circleci_image @@ -0,0 +1,58 @@ +FROM ubuntu:19.04 + +# Update and upgrade distribution +RUN apt-get update && \ + apt-get upgrade -y + +# Tools necessary for installing and configuring Ubuntu +RUN apt-get install -y \ + apt-utils \ + locales \ + tzdata + +# Timezone +RUN echo "Europe/Paris" | tee /etc/timezone && \ + ln -fs /usr/share/zoneinfo/Europe/Paris /etc/localtime && \ + dpkg-reconfigure -f noninteractive tzdata + +# Locale with UTF-8 support +RUN echo en_US.UTF-8 UTF-8 >> /etc/locale.gen && \ + locale-gen && \ + update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +# Required for Gudhi compilation +RUN apt-get install -y make \ + g++ \ + cmake \ + graphviz \ + perl \ + texlive-bibtex-extra \ + biber \ + doxygen \ + libboost-all-dev \ + libeigen3-dev \ + libgmp3-dev \ + libmpfr-dev \ + libtbb-dev \ + libcgal-dev \ + locales \ + python3 \ + python3-pip \ + python3-pytest \ + python3-tk \ + libfreetype6-dev \ + pkg-config + +RUN pip3 install \ + numpy \ + matplotlib \ + scipy \ + Cython \ + sphinx \ + sphinxcontrib-bibtex + +# apt clean up +RUN apt autoremove && rm -rf /var/lib/apt/lists/* diff --git a/Dockerfile_gudhi_installation b/Dockerfile_gudhi_installation new file mode 100644 index 00000000..9fe20730 --- /dev/null +++ b/Dockerfile_gudhi_installation @@ -0,0 +1,65 @@ +FROM ubuntu:19.04 + +# Update and upgrade distribution +RUN apt-get update && \ + apt-get upgrade -y + +# Tools necessary for installing and configuring Ubuntu +RUN apt-get install -y \ + apt-utils \ + locales \ + tzdata + +# Timezone +RUN echo "Europe/Paris" | tee /etc/timezone && \ + ln -fs /usr/share/zoneinfo/Europe/Paris /etc/localtime && \ + dpkg-reconfigure -f noninteractive tzdata + +# Locale with UTF-8 support +RUN echo en_US.UTF-8 UTF-8 >> /etc/locale.gen && \ + locale-gen && \ + update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +# Required for Gudhi compilation +RUN apt-get install -y make \ + g++ \ + cmake \ + graphviz \ + perl \ + texlive-bibtex-extra \ + biber \ + libboost-all-dev \ + libeigen3-dev \ + libgmp3-dev \ + libmpfr-dev \ + libtbb-dev \ + libcgal-dev \ + locales \ + python3 \ + python3-pip \ + python3-pytest \ + python3-tk \ + libfreetype6-dev \ + pkg-config \ + curl + +RUN pip3 install \ + numpy \ + matplotlib \ + scipy \ + Cython + +# apt clean up +RUN apt autoremove && rm -rf /var/lib/apt/lists/* + +RUN curl -LO "https://github.com/GUDHI/gudhi-devel/releases/download/tags%2Fgudhi-release-3.0.0/gudhi.3.0.0.tar.gz" \ +&& tar xf gudhi.3.0.0.tar.gz \ +&& cd gudhi.3.0.0 \ +&& mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_PYTHON=OFF -DPython_ADDITIONAL_VERSIONS=3 .. \ +&& make all test install \ +&& cmake -DWITH_GUDHI_PYTHON=ON . \ +&& cd python \ +&& python3 setup.py install \ No newline at end of file diff --git a/Dockerfile_ubuntu b/Dockerfile_ubuntu deleted file mode 100644 index e149a33a..00000000 --- a/Dockerfile_ubuntu +++ /dev/null @@ -1,58 +0,0 @@ -FROM ubuntu:19.04 - -# Update and upgrade distribution -RUN apt-get update && \ - apt-get upgrade -y - -# Tools necessary for installing and configuring Ubuntu -RUN apt-get install -y \ - apt-utils \ - locales \ - tzdata - -# Timezone -RUN echo "Europe/Paris" | tee /etc/timezone && \ - ln -fs /usr/share/zoneinfo/Europe/Paris /etc/localtime && \ - dpkg-reconfigure -f noninteractive tzdata - -# Locale with UTF-8 support -RUN echo en_US.UTF-8 UTF-8 >> /etc/locale.gen && \ - locale-gen && \ - update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en -ENV LC_ALL en_US.UTF-8 - -# Required for Gudhi compilation -RUN apt-get install -y make \ - g++ \ - cmake \ - graphviz \ - perl \ - texlive-bibtex-extra \ - biber \ - doxygen \ - libboost-all-dev \ - libeigen3-dev \ - libgmp3-dev \ - libmpfr-dev \ - libtbb-dev \ - libcgal-dev \ - locales \ - python3 \ - python3-pip \ - python3-pytest \ - python3-tk \ - libfreetype6-dev \ - pkg-config - -RUN pip3 install \ - numpy \ - matplotlib \ - scipy \ - Cython \ - sphinx \ - sphinxcontrib-bibtex - -# apt clean up -RUN apt autoremove && rm -rf /var/lib/apt/lists/* -- cgit v1.2.3 From e197ebc7a6996d0ed7c8b68e364e8a0484a21fbd Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 24 Sep 2019 13:33:33 +0200 Subject: Add POT for CI --- .appveyor.yml | 2 +- .travis.yml | 2 +- Dockerfile_ubuntu | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 6ed75cf7..6bfd4467 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -47,7 +47,7 @@ install: - python --version - pip --version - python -m pip install --upgrade pip - - pip install -U setuptools numpy matplotlib scipy Cython pytest + - pip install -U setuptools numpy matplotlib scipy Cython pytest POT build_script: - mkdir build diff --git a/.travis.yml b/.travis.yml index bf268057..bf2e919a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,7 +55,7 @@ before_cache: # When installing through libcgal-dev apt, CMake Error at CGAL Exports.cmake The imported target "CGAL::CGAL Qt5" references the file install: - python3 -m pip install --upgrade pip setuptools wheel - - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex matplotlib numpy scipy + - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex matplotlib numpy scipy POT script: - rm -rf build diff --git a/Dockerfile_ubuntu b/Dockerfile_ubuntu index e149a33a..12f2dc94 100644 --- a/Dockerfile_ubuntu +++ b/Dockerfile_ubuntu @@ -51,6 +51,7 @@ RUN pip3 install \ matplotlib \ scipy \ Cython \ + POT \ sphinx \ sphinxcontrib-bibtex -- cgit v1.2.3 From dd6bef28247a0e2aa2a0b024f475422314fabe64 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 24 Sep 2019 14:05:55 +0200 Subject: Modification rollback as it does not trigger Docker hub container construction --- Dockerfile_ubuntu | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile_ubuntu b/Dockerfile_ubuntu index 12f2dc94..e149a33a 100644 --- a/Dockerfile_ubuntu +++ b/Dockerfile_ubuntu @@ -51,7 +51,6 @@ RUN pip3 install \ matplotlib \ scipy \ Cython \ - POT \ sphinx \ sphinxcontrib-bibtex -- cgit v1.2.3 From 8cbeb21d32701e7ae848062116cae91833667900 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 24 Sep 2019 14:07:47 +0200 Subject: Add POT for wasserstein purpose --- Dockerfile_ubuntu | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile_ubuntu b/Dockerfile_ubuntu index e149a33a..12f2dc94 100644 --- a/Dockerfile_ubuntu +++ b/Dockerfile_ubuntu @@ -51,6 +51,7 @@ RUN pip3 install \ matplotlib \ scipy \ Cython \ + POT \ sphinx \ sphinxcontrib-bibtex -- cgit v1.2.3 From 824e34c83bbb8c2bc1ca0b1f7519fc77cae7f496 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 24 Sep 2019 14:39:09 +0200 Subject: Seems POT requires to be installed separatly from the other pip requirements --- .appveyor.yml | 3 ++- .travis.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 6bfd4467..125f3cf4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -47,7 +47,8 @@ install: - python --version - pip --version - python -m pip install --upgrade pip - - pip install -U setuptools numpy matplotlib scipy Cython pytest POT + - pip install -U setuptools numpy matplotlib scipy Cython pytest + - pip install -U POT build_script: - mkdir build diff --git a/.travis.yml b/.travis.yml index bf2e919a..27514ef5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,7 +55,8 @@ before_cache: # When installing through libcgal-dev apt, CMake Error at CGAL Exports.cmake The imported target "CGAL::CGAL Qt5" references the file install: - python3 -m pip install --upgrade pip setuptools wheel - - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex matplotlib numpy scipy POT + - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex matplotlib numpy scipy + - python3 -m pip install --user POT script: - rm -rf build -- cgit v1.2.3 From bbbba969e769277140920b17f0d92e4e00f1b904 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 24 Sep 2019 15:19:25 +0200 Subject: no python documentation generation if POT is not found --- src/python/CMakeLists.txt | 60 +++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 063a19e8..07931d10 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -65,6 +65,9 @@ if(PYTHONINTERP_FOUND) if(SCIPY_FOUND) add_gudhi_debug_info("Scipy version ${SCIPY_VERSION}") endif() + if(OT_FOUND) + add_gudhi_debug_info("POT version ${OT_VERSION}") + endif() set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_RESULT_OF_USE_DECLTYPE', ") set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_ALL_NO_LIB', ") @@ -375,7 +378,7 @@ if(PYTHONINTERP_FOUND) # Wasserstein if(OT_FOUND) - add_gudhi_py_test(test_wasserstein_distance) + add_gudhi_py_test(test_wasserstein_distance) endif(OT_FOUND) # Documentation generation is available through sphinx - requires all modules @@ -383,32 +386,37 @@ if(PYTHONINTERP_FOUND) if(MATPLOTLIB_FOUND) if(NUMPY_FOUND) if(SCIPY_FOUND) - if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/") - # User warning - Sphinx is a static pages generator, and configured to work fine with user_version - # Images and biblio warnings because not found on developper version - if (GUDHI_PYTHON_PATH STREQUAL "src/python") - set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developper version. Images and biblio will miss") - endif() - # sphinx target requires gudhi.so, because conf.py reads gudhi version from it - add_custom_target(sphinx - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gudhi.so" - COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM) - - add_test(NAME sphinx_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest) - - # Set missing or not modules - set(GUDHI_MODULES ${GUDHI_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MODULES") - else(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - message("++ Python documentation module will not be compiled because it requires a Eigen3 and CGAL version >= 4.11.0") + if(OT_FOUND) + if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/") + # User warning - Sphinx is a static pages generator, and configured to work fine with user_version + # Images and biblio warnings because not found on developper version + if (GUDHI_PYTHON_PATH STREQUAL "src/python") + set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developper version. Images and biblio will miss") + endif() + # sphinx target requires gudhi.so, because conf.py reads gudhi version from it + add_custom_target(sphinx + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gudhi.so" + COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM) + + add_test(NAME sphinx_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest) + + # Set missing or not modules + set(GUDHI_MODULES ${GUDHI_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MODULES") + else(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + message("++ Python documentation module will not be compiled because it requires a Eigen3 and CGAL version >= 4.11.0") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") + endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + else(OT_FOUND) + message("++ Python documentation module will not be compiled because POT was not found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") - endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + endif(OT_FOUND) else(SCIPY_FOUND) message("++ Python documentation module will not be compiled because scipy was not found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") -- cgit v1.2.3 From 6e4b12c89f2c5b32c19076878d86212ffa27e817 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 2 Oct 2019 11:34:48 +0200 Subject: Fix DOI Fix #99 --- biblio/bibliography.bib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biblio/bibliography.bib b/biblio/bibliography.bib index d1b2f558..a1b951e0 100644 --- a/biblio/bibliography.bib +++ b/biblio/bibliography.bib @@ -1076,7 +1076,7 @@ language={English} journal = {Computational Geometry: Theory and Applications}, volume = {58}, pages = {70--96}, - doi = "https://doi.org/10.1016/j.comgeo.2016.07.001", + doi = "10.1016/j.comgeo.2016.07.001", year = {2016} } -- cgit v1.2.3 From eb94efd322862ee32775493ad8b389682dadc75e Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 9 Oct 2019 10:54:31 +0900 Subject: Fix wikipedia link for Cech complex --- src/Cech_complex/doc/Intro_cech_complex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cech_complex/doc/Intro_cech_complex.h b/src/Cech_complex/doc/Intro_cech_complex.h index 90086de7..80c88dc6 100644 --- a/src/Cech_complex/doc/Intro_cech_complex.h +++ b/src/Cech_complex/doc/Intro_cech_complex.h @@ -24,7 +24,7 @@ namespace cech_complex { * \section cechdefinition Čech complex definition * * Čech complex - * (Wikipedia) is a + * (Wikipedia) is a * simplicial complex constructed * from a proximity graph. The set of all simplices is filtered by the radius of their minimal enclosing ball. * -- cgit v1.2.3 From 470ee54e1bdb0ff074db9213412766476ccf51fb Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 10 Oct 2019 10:03:21 +0200 Subject: Add maximal edge length parameter for sparse rips persistence utility --- src/Rips_complex/utilities/ripscomplex.md | 1 + src/Rips_complex/utilities/sparse_rips_persistence.cpp | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Rips_complex/utilities/ripscomplex.md b/src/Rips_complex/utilities/ripscomplex.md index 03838085..61f31e3c 100644 --- a/src/Rips_complex/utilities/ripscomplex.md +++ b/src/Rips_complex/utilities/ripscomplex.md @@ -99,6 +99,7 @@ where `dim` is the dimension of the homological feature, `birth` and `death` are * `-h [ --help ]` Produce help message * `-o [ --output-file ]` Name of file in which the persistence diagram is written. Default print in standard output. +* `-r [ --max-edge-length ]` (default = inf) Maximal length of an edge for the Rips complex construction. * `-e [ --approximation ]` (default = .5) Epsilon, where the sparse Rips complex is a (1+epsilon)/(1-epsilon)-approximation of the Rips complex. * `-d [ --cpx-dimension ]` (default = INT_MAX) Maximal dimension of the Rips complex we want to compute. * `-p [ --field-charac ]` (default = 11) Characteristic p of the coefficient field Z/pZ for computing homology. diff --git a/src/Rips_complex/utilities/sparse_rips_persistence.cpp b/src/Rips_complex/utilities/sparse_rips_persistence.cpp index 1a86eafe..cefd8a67 100644 --- a/src/Rips_complex/utilities/sparse_rips_persistence.cpp +++ b/src/Rips_complex/utilities/sparse_rips_persistence.cpp @@ -28,21 +28,24 @@ using Persistent_cohomology = Gudhi::persistent_cohomology::Persistent_cohomolog using Point = std::vector; using Points_off_reader = Gudhi::Points_off_reader; -void program_options(int argc, char* argv[], std::string& off_file_points, std::string& filediag, double& epsilon, +void program_options(int argc, char* argv[], std::string& off_file_points, std::string& filediag, + Filtration_value& threshold, double& epsilon, int& dim_max, int& p, Filtration_value& min_persistence); int main(int argc, char* argv[]) { std::string off_file_points; std::string filediag; + Filtration_value threshold; double epsilon; int dim_max; int p; Filtration_value min_persistence; - program_options(argc, argv, off_file_points, filediag, epsilon, dim_max, p, min_persistence); + program_options(argc, argv, off_file_points, filediag, threshold, epsilon, dim_max, p, min_persistence); Points_off_reader off_reader(off_file_points); - Sparse_rips sparse_rips(off_reader.get_point_cloud(), Gudhi::Euclidean_distance(), epsilon); + Sparse_rips sparse_rips(off_reader.get_point_cloud(), Gudhi::Euclidean_distance(), epsilon, + -std::numeric_limits::infinity(), threshold); // Construct the Rips complex in a Simplex Tree Simplex_tree simplex_tree; @@ -73,7 +76,8 @@ int main(int argc, char* argv[]) { return 0; } -void program_options(int argc, char* argv[], std::string& off_file_points, std::string& filediag, double& epsilon, +void program_options(int argc, char* argv[], std::string& off_file_points, std::string& filediag, + Filtration_value& threshold, double& epsilon, int& dim_max, int& p, Filtration_value& min_persistence) { namespace po = boost::program_options; po::options_description hidden("Hidden options"); @@ -84,6 +88,9 @@ void program_options(int argc, char* argv[], std::string& off_file_points, std:: visible.add_options()("help,h", "produce help message")( "output-file,o", po::value(&filediag)->default_value(std::string()), "Name of file in which the persistence diagram is written. Default print in std::cout")( + "max-edge-length,r", + po::value(&threshold)->default_value(std::numeric_limits::infinity()), + "Maximal length of an edge for the Rips complex construction.")( "approximation,e", po::value(&epsilon)->default_value(.5), "Epsilon, where the sparse Rips complex is a (1+epsilon)-approximation of the Rips complex.")( "cpx-dimension,d", po::value(&dim_max)->default_value(std::numeric_limits::max()), -- cgit v1.2.3 From b1824e4de6fd1d037af3c1341c3065731472ffc8 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau <10407034+VincentRouvreau@users.noreply.github.com> Date: Tue, 15 Oct 2019 17:37:01 +0200 Subject: Add conda package badges --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8636ac77..167a38b3 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![Build Status](https://travis-ci.org/GUDHI/gudhi-devel.svg?branch=master)](https://travis-ci.org/GUDHI/gudhi-devel) [![CircleCI](https://circleci.com/gh/GUDHI/gudhi-devel/tree/master.svg?style=svg)](https://circleci.com/gh/GUDHI/gudhi-devel/tree/master) [![Build status](https://ci.appveyor.com/api/projects/status/976j2uut8xgalvx2/branch/master?svg=true)](https://ci.appveyor.com/project/GUDHI/gudhi-devel/branch/master) +[![Anaconda Cloud](https://anaconda.org/conda-forge/gudhi/badges/version.svg)](https://anaconda.org/conda-forge/gudhi) +[![Anaconda downloads](https://anaconda.org/conda-forge/gudhi/badges/downloads.svg)](https://anaconda.org/conda-forge/gudhi) ![GUDHI](src/common/doc/Gudhi_banner.png "Topological Data Analysis (TDA) and Higher Dimensional Geometry Understanding") -- cgit v1.2.3 From f9ec015c1bdd01068771d0d04ff55e0436ffc879 Mon Sep 17 00:00:00 2001 From: tlacombe Date: Thu, 17 Oct 2019 11:55:22 +0200 Subject: updated index.rst to include Wasserstein distance --- src/python/doc/index.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index e379bc23..16d918bc 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -73,6 +73,11 @@ Bottleneck distance .. include:: bottleneck_distance_sum.inc +Wasserstein distance +=================== + +.. include:: wasserstein_distance_sum.inc + Persistence graphical tools =========================== -- cgit v1.2.3 From 91632989f92b89752dd4e59836dff80b43f349f1 Mon Sep 17 00:00:00 2001 From: tlacombe Date: Thu, 17 Oct 2019 11:56:20 +0200 Subject: updated wasserstein doc to be sphinx-compatible + correction of typo in the .rst --- src/python/doc/wasserstein_distance_sum.inc | 4 ++-- src/python/gudhi/wasserstein.py | 32 +++++++++++++++-------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/python/doc/wasserstein_distance_sum.inc b/src/python/doc/wasserstein_distance_sum.inc index 0263f80f..3b0b9025 100644 --- a/src/python/doc/wasserstein_distance_sum.inc +++ b/src/python/doc/wasserstein_distance_sum.inc @@ -7,8 +7,8 @@ | :figclass: align-center | a perfect matching between the points of the two diagrams (+ all the | :Introduced in: GUDHI 2.0.0 | | | diagonal points), where the value of a matching is defined as the | | | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edges lengths to the power p. Edges | :Copyright: MIT (`GPL v3 `_) | - | edges lengths to the power p. | lengths are measured in norm q, for $1 \leq q \leq \infty$. | | - | | | :Requires: `Python Optimal Transport (POT)` | + | edges lengths to the power p. | lengths are measured in norm q, for :math:`1 \leq q \leq \infty`. | | + | | | :Requires: Python Optimal Transport (POT) :math:`\geq` 0.5.1 | +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ | * :doc:`wasserstein_distance_user` | | +-----------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index ae5f75a5..0b2fe79a 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -17,8 +17,8 @@ except ImportError: def _proj_on_diag(X): ''' - param X: (n x 2) array encoding the points of a persistent diagram. - return: (n x 2) arary encoding the (respective orthogonal) projections of the points onto the diagonal + :param X: (n x 2) array encoding the points of a persistent diagram. + :returns: (n x 2) arary encoding the (respective orthogonal) projections of the points onto the diagonal ''' Z = (X[:,0] + X[:,1]) / 2. return np.array([Z , Z]).T @@ -26,11 +26,11 @@ def _proj_on_diag(X): def _build_dist_matrix(X, Y, p=2., q=2.): ''' - param X: (n x 2) np.array encoding the (points of the) first diagram. - param Y: (m x 2) np.array encoding the second diagram. - param q: Ground metric (i.e. norm l_q). - param p: exponent for the Wasserstein metric. - return: (n+1) x (m+1) np.array encoding the cost matrix C. + :param X: (n x 2) np.array encoding the (points of the) first diagram. + :param Y: (m x 2) np.array encoding the second diagram. + :param q: Ground metric (i.e. norm l_q). + :param p: exponent for the Wasserstein metric. + :returns: (n+1) x (m+1) np.array encoding the cost matrix C. For 1 <= i <= n, 1 <= j <= m, C[i,j] encodes the distance between X[i] and Y[j], while C[i, m+1] (resp. C[n+1, j]) encodes the distance (to the p) between X[i] (resp Y[j]) and its orthogonal proj onto the diagonal. note also that C[n+1, m+1] = 0 (it costs nothing to move from the diagonal to the diagonal). ''' @@ -54,10 +54,10 @@ def _build_dist_matrix(X, Y, p=2., q=2.): def _perstot(X, p, q): ''' - param X: (n x 2) numpy array (points of a given diagram) - param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). - param p: exponent for Wasserstein; Default value is 2. - return: float, the total persistence of the diagram (that is, its distance to the empty diagram). + :param X: (n x 2) numpy array (points of a given diagram) + :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). + :param p: exponent for Wasserstein; Default value is 2. + :returns: float, the total persistence of the diagram (that is, its distance to the empty diagram). ''' Xdiag = _proj_on_diag(X) return (np.sum(np.linalg.norm(X - Xdiag, ord=q, axis=1)**p))**(1/p) @@ -65,10 +65,12 @@ def _perstot(X, p, q): def wasserstein_distance(X, Y, p=2., q=2.): ''' - param X, Y: (n x 2) and (m x 2) numpy array (points of persistence diagrams) - param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). - param p: exponent for Wasserstein; Default value is 2. - return: float, the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. + :param X: (n x 2) np.array encoding the (points of the) first diagram. + :param Y: (m x 2) np.array encoding the second diagram. + :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). + :param p: exponent for Wasserstein; Default value is 2. + :returns: the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. + :rtype: float ''' n = len(X) m = len(Y) -- cgit v1.2.3 From a6c844172072f1798bbbe6d5c9b4f3a548cb4c50 Mon Sep 17 00:00:00 2001 From: Siargey Kachanovich Date: Thu, 17 Oct 2019 14:21:07 +0200 Subject: Include for std::fabs --- src/common/include/gudhi/Unitary_tests_utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/include/gudhi/Unitary_tests_utils.h b/src/common/include/gudhi/Unitary_tests_utils.h index 4ad4dae8..7d039304 100644 --- a/src/common/include/gudhi/Unitary_tests_utils.h +++ b/src/common/include/gudhi/Unitary_tests_utils.h @@ -14,6 +14,7 @@ #include #include // for std::numeric_limits<> +#include // for std::fabs template void GUDHI_TEST_FLOAT_EQUALITY_CHECK(FloatingType a, FloatingType b, -- cgit v1.2.3 From ddf16037ca92675a5f13ea84d1f3c94467932dc7 Mon Sep 17 00:00:00 2001 From: Jose Marino Date: Thu, 17 Oct 2019 15:01:23 -0600 Subject: fix documentation not being generated with Doxygen 1.8.16 --- src/Doxyfile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Doxyfile.in b/src/Doxyfile.in index 57775498..ec551882 100644 --- a/src/Doxyfile.in +++ b/src/Doxyfile.in @@ -765,7 +765,7 @@ INPUT_ENCODING = UTF-8 # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. -FILE_PATTERNS = +#FILE_PATTERNS = # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. -- cgit v1.2.3 From 7ea7dfe83e93dc3d33d8d50917f718c05f32ca7f Mon Sep 17 00:00:00 2001 From: tlacombe Date: Thu, 17 Oct 2019 23:54:39 +0200 Subject: removed GPL license, updated expected gudhi release (set at 3.1.0 for now) --- src/python/doc/wasserstein_distance_sum.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/doc/wasserstein_distance_sum.inc b/src/python/doc/wasserstein_distance_sum.inc index 3b0b9025..24b72c0e 100644 --- a/src/python/doc/wasserstein_distance_sum.inc +++ b/src/python/doc/wasserstein_distance_sum.inc @@ -4,9 +4,9 @@ +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ | .. figure:: | The p-Wasserstein distance measures the similarity between two | :Author: Theo Lacombe | | ../../doc/Bottleneck_distance/perturb_pd.png | persistence diagrams. It's the minimum value c that can be achieve by| | - | :figclass: align-center | a perfect matching between the points of the two diagrams (+ all the | :Introduced in: GUDHI 2.0.0 | + | :figclass: align-center | a perfect matching between the points of the two diagrams (+ all the | :Introduced in: GUDHI 3.1.0 | | | diagonal points), where the value of a matching is defined as the | | - | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edges lengths to the power p. Edges | :Copyright: MIT (`GPL v3 `_) | + | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edges lengths to the power p. Edges | :Copyright: MIT | | edges lengths to the power p. | lengths are measured in norm q, for :math:`1 \leq q \leq \infty`. | | | | | :Requires: Python Optimal Transport (POT) :math:`\geq` 0.5.1 | +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ -- cgit v1.2.3 From a625d5879beaaae47118bc79fda17f441483dffe Mon Sep 17 00:00:00 2001 From: tlacombe Date: Thu, 17 Oct 2019 23:55:17 +0200 Subject: Few improvements to the documentation. --- src/python/gudhi/wasserstein.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index 0b2fe79a..4e17811d 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -26,8 +26,8 @@ def _proj_on_diag(X): def _build_dist_matrix(X, Y, p=2., q=2.): ''' - :param X: (n x 2) np.array encoding the (points of the) first diagram. - :param Y: (m x 2) np.array encoding the second diagram. + :param X: (n x 2) numpy.array encoding the (points of the) first diagram. + :param Y: (m x 2) numpy.array encoding the second diagram. :param q: Ground metric (i.e. norm l_q). :param p: exponent for the Wasserstein metric. :returns: (n+1) x (m+1) np.array encoding the cost matrix C. @@ -54,19 +54,19 @@ def _build_dist_matrix(X, Y, p=2., q=2.): def _perstot(X, p, q): ''' - :param X: (n x 2) numpy array (points of a given diagram) - :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). + :param X: (n x 2) numpy.array (points of a given diagram). + :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (Euclidean norm). :param p: exponent for Wasserstein; Default value is 2. :returns: float, the total persistence of the diagram (that is, its distance to the empty diagram). ''' Xdiag = _proj_on_diag(X) - return (np.sum(np.linalg.norm(X - Xdiag, ord=q, axis=1)**p))**(1/p) + return (np.sum(np.linalg.norm(X - Xdiag, ord=q, axis=1)**p))**(1./p) def wasserstein_distance(X, Y, p=2., q=2.): ''' - :param X: (n x 2) np.array encoding the (points of the) first diagram. - :param Y: (m x 2) np.array encoding the second diagram. + :param X: (n x 2) numpy.array encoding the (finite points of the) first diagram. Must not contain essential points (i.e. with infinite coordinate). + :param Y: (m x 2) numpy.array encoding the second diagram. :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). :param p: exponent for Wasserstein; Default value is 2. :returns: the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. -- cgit v1.2.3 From a3e609d04f296f80edf622ba1d6e0efe71eb8c41 Mon Sep 17 00:00:00 2001 From: Théo Lacombe Date: Fri, 18 Oct 2019 22:17:48 +0200 Subject: Update src/python/gudhi/wasserstein.py Co-Authored-By: Marc Glisse --- src/python/gudhi/wasserstein.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index 4e17811d..32d236c5 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -69,7 +69,7 @@ def wasserstein_distance(X, Y, p=2., q=2.): :param Y: (m x 2) numpy.array encoding the second diagram. :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). :param p: exponent for Wasserstein; Default value is 2. - :returns: the p-Wasserstein distance (1 <= p < infty) with respect to the q-norm as ground metric. + :returns: the p-Wasserstein distance (1 <= p < infinity) with respect to the q-norm as ground metric. :rtype: float ''' n = len(X) -- cgit v1.2.3 From 12f62eca36952053169a71f70169a0e15fa481fc Mon Sep 17 00:00:00 2001 From: tlacombe Date: Fri, 18 Oct 2019 22:30:07 +0200 Subject: correction edges lengths ==> edge lengths --- src/python/doc/wasserstein_distance_sum.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python/doc/wasserstein_distance_sum.inc b/src/python/doc/wasserstein_distance_sum.inc index 24b72c0e..ffd4d312 100644 --- a/src/python/doc/wasserstein_distance_sum.inc +++ b/src/python/doc/wasserstein_distance_sum.inc @@ -3,11 +3,11 @@ +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ | .. figure:: | The p-Wasserstein distance measures the similarity between two | :Author: Theo Lacombe | - | ../../doc/Bottleneck_distance/perturb_pd.png | persistence diagrams. It's the minimum value c that can be achieve by| | - | :figclass: align-center | a perfect matching between the points of the two diagrams (+ all the | :Introduced in: GUDHI 3.1.0 | + | ../../doc/Bottleneck_distance/perturb_pd.png | persistence diagrams. It's the minimum value c that can be achieved | | + | :figclass: align-center | by a perfect matching between the points of the two diagrams (+ all | :Introduced in: GUDHI 3.1.0 | | | diagonal points), where the value of a matching is defined as the | | - | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edges lengths to the power p. Edges | :Copyright: MIT | - | edges lengths to the power p. | lengths are measured in norm q, for :math:`1 \leq q \leq \infty`. | | + | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edge lengths to the power p. Edge lengths| :Copyright: MIT | + | edge lengths to the power p. | are measured in norm q, for :math:`1 \leq q \leq \infty`. | | | | | :Requires: Python Optimal Transport (POT) :math:`\geq` 0.5.1 | +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ | * :doc:`wasserstein_distance_user` | | -- cgit v1.2.3 From e0feb6725f3970eea66c8e3268ec1a55020279bd Mon Sep 17 00:00:00 2001 From: Théo Lacombe Date: Fri, 18 Oct 2019 22:31:38 +0200 Subject: Update src/python/gudhi/wasserstein.py typo correction Co-Authored-By: Marc Glisse --- src/python/gudhi/wasserstein.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index 32d236c5..445772e4 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -18,7 +18,7 @@ except ImportError: def _proj_on_diag(X): ''' :param X: (n x 2) array encoding the points of a persistent diagram. - :returns: (n x 2) arary encoding the (respective orthogonal) projections of the points onto the diagonal + :returns: (n x 2) array encoding the (respective orthogonal) projections of the points onto the diagonal ''' Z = (X[:,0] + X[:,1]) / 2. return np.array([Z , Z]).T -- cgit v1.2.3 From 0c6641b4e109f5116d6dad04bbab9bde0d56e347 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 24 Oct 2019 08:24:24 +0200 Subject: Add debug traces for read_persistence_intervals_and_dimension --- src/common/include/gudhi/reader_utils.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common/include/gudhi/reader_utils.h b/src/common/include/gudhi/reader_utils.h index 98335552..ee811967 100644 --- a/src/common/include/gudhi/reader_utils.h +++ b/src/common/include/gudhi/reader_utils.h @@ -305,8 +305,12 @@ void read_persistence_intervals_and_dimension(std::string const& filename, Outpu std::string line; getline(in, line); if (line.length() != 0 && line[0] != '#') { - double numbers[4]; + double numbers[4] = {0.}; int n = sscanf(line.c_str(), "%lf %lf %lf %lf", &numbers[0], &numbers[1], &numbers[2], &numbers[3]); +#ifdef DEBUG_TRACES + std::cout << "[" << n << "] = " << numbers[0] << "," << numbers[1] + << "," << numbers[2] << "," << numbers[3] << std::endl; +#endif // DEBUG_TRACES if (n >= 2) { int dim = (n >= 3 ? static_cast(numbers[n - 3]) : -1); *out++ = std::make_tuple(dim, numbers[n - 2], numbers[n - 1]); -- cgit v1.2.3 From 8227cd68d5aa7c9eeda5dd474f2536b896b6f491 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 24 Oct 2019 08:30:06 +0200 Subject: Fix issues #113 (replace 'is not' with '!=') and #109 (replace palette with a more visible one) --- src/python/gudhi/persistence_graphical_tools.py | 57 +++++++++++-------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/src/python/gudhi/persistence_graphical_tools.py b/src/python/gudhi/persistence_graphical_tools.py index 181bc8ea..a8e2051b 100644 --- a/src/python/gudhi/persistence_graphical_tools.py +++ b/src/python/gudhi/persistence_graphical_tools.py @@ -44,27 +44,6 @@ def __min_birth_max_death(persistence, band=0.0): max_death += band return (min_birth, max_death) - -""" -Only 13 colors for the palette -""" -palette = [ - "#ff0000", - "#00ff00", - "#0000ff", - "#00ffff", - "#ff00ff", - "#ffff00", - "#000000", - "#880000", - "#008800", - "#000088", - "#888800", - "#880088", - "#008888", -] - - def plot_persistence_barcode( persistence=[], persistence_file="", @@ -73,6 +52,7 @@ def plot_persistence_barcode( max_barcodes=1000, inf_delta=0.1, legend=False, + colormap=None ): """This function plots the persistence bar code from persistence values list or from a :doc:`persistence file `. @@ -95,6 +75,9 @@ def plot_persistence_barcode( :type inf_delta: float. :param legend: Display the dimension color legend (default is False). :type legend: boolean. + :param colormap: A matplotlib-like qualitative colormaps. Default is None + which means :code:`matplotlib.cm.Set1.colors`. + :type colormap: tuple of colors (3-tuple of float between 0. and 1.). :returns: A matplotlib object containing horizontal bar plot of persistence (launch `show()` method on it to display it). """ @@ -102,7 +85,7 @@ def plot_persistence_barcode( import matplotlib.pyplot as plt import matplotlib.patches as mpatches - if persistence_file is not "": + if persistence_file != "": if path.isfile(persistence_file): # Reset persistence persistence = [] @@ -116,7 +99,7 @@ def plot_persistence_barcode( print("file " + persistence_file + " not found.") return None - if max_barcodes is not 1000: + if max_barcodes != 1000: print("Deprecated parameter. It has been replaced by max_intervals") max_intervals = max_barcodes @@ -127,6 +110,9 @@ def plot_persistence_barcode( key=lambda life_time: life_time[1][1] - life_time[1][0], reverse=True, )[:max_intervals] + + if colormap == None: + colormap = plt.cm.Set1.colors persistence = sorted(persistence, key=lambda birth: birth[1][0]) @@ -147,7 +133,7 @@ def plot_persistence_barcode( height=0.8, left=interval[1][0], alpha=alpha, - color=palette[interval[0]], + color=colormap[interval[0]], linewidth=0, ) else: @@ -158,7 +144,7 @@ def plot_persistence_barcode( height=0.8, left=interval[1][0], alpha=alpha, - color=palette[interval[0]], + color=colormap[interval[0]], linewidth=0, ) ind = ind + 1 @@ -167,7 +153,7 @@ def plot_persistence_barcode( dimensions = list(set(item[0] for item in persistence)) plt.legend( handles=[ - mpatches.Patch(color=palette[dim], label=str(dim)) + mpatches.Patch(color=colormap[dim], label=str(dim)) for dim in dimensions ], loc="lower right", @@ -190,6 +176,7 @@ def plot_persistence_diagram( max_plots=1000, inf_delta=0.1, legend=False, + colormap=None ): """This function plots the persistence diagram from persistence values list or from a :doc:`persistence file `. @@ -214,6 +201,9 @@ def plot_persistence_diagram( :type inf_delta: float. :param legend: Display the dimension color legend (default is False). :type legend: boolean. + :param colormap: A matplotlib-like qualitative colormaps. Default is None + which means :code:`matplotlib.cm.Set1.colors`. + :type colormap: tuple of colors (3-tuple of float between 0. and 1.). :returns: A matplotlib object containing diagram plot of persistence (launch `show()` method on it to display it). """ @@ -221,7 +211,7 @@ def plot_persistence_diagram( import matplotlib.pyplot as plt import matplotlib.patches as mpatches - if persistence_file is not "": + if persistence_file != "": if path.isfile(persistence_file): # Reset persistence persistence = [] @@ -235,7 +225,7 @@ def plot_persistence_diagram( print("file " + persistence_file + " not found.") return None - if max_plots is not 1000: + if max_plots != 1000: print("Deprecated parameter. It has been replaced by max_intervals") max_intervals = max_plots @@ -247,6 +237,9 @@ def plot_persistence_diagram( reverse=True, )[:max_intervals] + if colormap == None: + colormap = plt.cm.Set1.colors + (min_birth, max_death) = __min_birth_max_death(persistence, band) delta = (max_death - min_birth) * inf_delta # Replace infinity values with max_death + delta for diagram to be more @@ -272,19 +265,19 @@ def plot_persistence_diagram( interval[1][0], interval[1][1], alpha=alpha, - color=palette[interval[0]], + color=colormap[interval[0]], ) else: # Infinite death case for diagram to be nicer plt.scatter( - interval[1][0], infinity, alpha=alpha, color=palette[interval[0]] + interval[1][0], infinity, alpha=alpha, color=colormap[interval[0]] ) if legend: dimensions = list(set(item[0] for item in persistence)) plt.legend( handles=[ - mpatches.Patch(color=palette[dim], label=str(dim)) + mpatches.Patch(color=colormap[dim], label=str(dim)) for dim in dimensions ] ) @@ -354,7 +347,7 @@ def plot_persistence_density( import matplotlib.pyplot as plt from scipy.stats import kde - if persistence_file is not "": + if persistence_file != "": if dimension is None: # All dimension case dimension = -1 -- cgit v1.2.3 From 87dc932aeb793b52a6009d39c7580c5f56133511 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 25 Oct 2019 17:28:25 +0200 Subject: Add token doc file so sphinx builds the doc. --- src/python/doc/sktda.rst | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/python/doc/sktda.rst diff --git a/src/python/doc/sktda.rst b/src/python/doc/sktda.rst new file mode 100644 index 00000000..d36d9ab4 --- /dev/null +++ b/src/python/doc/sktda.rst @@ -0,0 +1,35 @@ +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +=================================== +sktda reference manual +=================================== + +Preprocessing +------------- +.. automodule:: gudhi.sktda.preprocessing + :members: + :undoc-members: + :show-inheritance: + +Vector methods +-------------- +.. automodule:: gudhi.sktda.vector_methods + :members: + :undoc-members: + :show-inheritance: + +Kernel methods +-------------- +.. automodule:: gudhi.sktda.kernel_methods + :members: + :undoc-members: + :show-inheritance: + +Metrics +------- +.. automodule:: gudhi.sktda.metrics + :members: + :undoc-members: + :show-inheritance: -- cgit v1.2.3 From 6609f46ac09e2fc08723177ad2bea1ce363aa4fa Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 09:00:49 +0100 Subject: Add interpreter to example --- src/python/example/ex_diagrams.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py index 3dc6fe84..a3efbac9 100644 --- a/src/python/example/ex_diagrams.py +++ b/src/python/example/ex_diagrams.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + import matplotlib.pyplot as plt import numpy as np from sklearn.kernel_approximation import RBFSampler -- cgit v1.2.3 From c5eadabc2bd2df02ad80141b480492e59a88df4e Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 09:01:16 +0100 Subject: Make example executable --- src/python/example/ex_diagrams.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 src/python/example/ex_diagrams.py diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py old mode 100644 new mode 100755 -- cgit v1.2.3 From 753dfe850f87bd1bb6bcb62741cf79697c4f2abd Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 10:17:43 +0100 Subject: Let cmake detect sklearn Not used yet, but it should protect the doc and future tests. --- src/cmake/modules/GUDHI_third_party_libraries.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index 360a230b..d053e94f 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -125,6 +125,7 @@ if( PYTHONINTERP_FOUND ) find_python_module("numpy") find_python_module("scipy") find_python_module("sphinx") + find_python_module("sklearn") endif() if(NOT GUDHI_PYTHON_PATH) -- cgit v1.2.3 From d02296df9ca6ebea31345a89a6070258c0ed91a9 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 12:59:55 +0100 Subject: Mention POT in installation instructions --- src/python/doc/installation.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 5a6ad9f4..4778b3d9 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -215,6 +215,13 @@ The following examples require the `Matplotlib `_: * :download:`euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py>` * :download:`euclidean_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py>` +Python Optimal Transport +======================== + +The :doc:`Wasserstein distance ` +module requires `POT `_, a library that provides +several solvers for optimization problems related to Optimal Transport. + SciPy ===== -- cgit v1.2.3 From 3c76f73a530daacd48d476cd96bd946e4ab6d78a Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 13:40:23 +0100 Subject: Wasserstein also uses SciPy --- src/python/doc/installation.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 4778b3d9..3711ca8e 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -225,9 +225,10 @@ several solvers for optimization problems related to Optimal Transport. SciPy ===== -The :doc:`persistence graphical tools ` -module requires `SciPy `_, a Python-based ecosystem of -open-source software for mathematics, science, and engineering. +The :doc:`persistence graphical tools ` and +:doc:`Wasserstein distance ` modules require `SciPy +`_, a Python-based ecosystem of open-source software for +mathematics, science, and engineering. Threading Building Blocks ========================= -- cgit v1.2.3 From b23f5dca9a0805d655ee46fac49e5b971a2cf9cd Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sun, 27 Oct 2019 14:02:07 +0100 Subject: Print sklearn version next to the other modules --- src/python/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 26cd6ec1..2756d547 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -65,6 +65,9 @@ if(PYTHONINTERP_FOUND) if(SCIPY_FOUND) add_gudhi_debug_info("Scipy version ${SCIPY_VERSION}") endif() + if(SKLEARN_FOUND) + add_gudhi_debug_info("Scikit-learn version ${SKLEARN_VERSION}") + endif() set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_RESULT_OF_USE_DECLTYPE', ") set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_ALL_NO_LIB', ") -- cgit v1.2.3 From ee4934750e8c9dbdee4874d56921aeb9bf7b7bb7 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 31 Oct 2019 08:48:15 +0100 Subject: Increase numItermax in the call to POT. This number is pretty arbitrary... --- src/python/gudhi/wasserstein.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index 445772e4..eba7c6d5 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -92,7 +92,8 @@ def wasserstein_distance(X, Y, p=2., q=2.): # Comptuation of the otcost using the ot.emd2 library. # Note: it is the squared Wasserstein distance. - ot_cost = (n+m) * ot.emd2(a, b, M) + # The default numItermax=100000 is not sufficient for some examples with 5000 points, what is a good value? + ot_cost = (n+m) * ot.emd2(a, b, M, numItermax=2000000) return ot_cost ** (1./p) -- cgit v1.2.3 From f6a78c1b7ea70b71fdb96f2fc17c44700e4b980a Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 31 Oct 2019 11:48:57 +0100 Subject: Add a get_point method for Alpha_complex_3d. Change Point_3 type to be either Weighted_point_3 or Bare_point_3 (new type). Add some get_point method tests. --- .../benchmark/Alpha_complex_3d_benchmark.cpp | 10 +-- .../example/Alpha_complex_3d_from_points.cpp | 2 +- .../Weighted_alpha_complex_3d_from_points.cpp | 12 ++-- src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 77 +++++++++++++++------- .../test/Alpha_complex_3d_unit_test.cpp | 53 ++++++++++++++- .../test/Periodic_alpha_complex_3d_unit_test.cpp | 37 +++++++---- .../test/Weighted_alpha_complex_3d_unit_test.cpp | 50 +++++++++----- ...eighted_periodic_alpha_complex_3d_unit_test.cpp | 24 +++---- .../utilities/alpha_complex_3d_persistence.cpp | 4 +- 9 files changed, 189 insertions(+), 80 deletions(-) diff --git a/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp b/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp index 005a712a..e84f5229 100644 --- a/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp +++ b/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp @@ -71,10 +71,10 @@ void benchmark_points_on_torus_3D(const std::string& msg) { for (int nb_points = 1000; nb_points <= 125000; nb_points *= 5) { std::cout << " Alpha complex 3d on torus with " << nb_points << " points." << std::endl; std::vector points_on_torus = Gudhi::generate_points_on_torus_3D(nb_points, 1.0, 0.5); - std::vector points; + std::vector points; for (auto p : points_on_torus) { - points.push_back(typename Alpha_complex_3d::Point_3(p[0], p[1], p[2])); + points.push_back(typename Alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); } Gudhi::Clock ac_create_clock(" benchmark_points_on_torus_3D - Alpha complex 3d creation"); @@ -115,7 +115,7 @@ void benchmark_weighted_points_on_torus_3D(const std::string& msg) { std::cout << " Alpha complex 3d on torus with " << nb_points << " points." << std::endl; std::vector points_on_torus = Gudhi::generate_points_on_torus_3D(nb_points, 1.0, 0.5); - using Point = typename Weighted_alpha_complex_3d::Point_3; + using Point = typename Weighted_alpha_complex_3d::Bare_point_3; using Weighted_point = typename Weighted_alpha_complex_3d::Weighted_point_3; std::vector points; @@ -158,7 +158,7 @@ void benchmark_periodic_points(const std::string& msg) { for (double nb_points = 10.; nb_points <= 40.; nb_points += 10.) { std::cout << " Periodic alpha complex 3d with " << nb_points * nb_points * nb_points << " points." << std::endl; - using Point = typename Periodic_alpha_complex_3d::Point_3; + using Point = typename Periodic_alpha_complex_3d::Bare_point_3; std::vector points; for (double i = 0; i < nb_points; i++) { @@ -206,7 +206,7 @@ void benchmark_weighted_periodic_points(const std::string& msg) { std::cout << " Weighted periodic alpha complex 3d with " << nb_points * nb_points * nb_points << " points." << std::endl; - using Point = typename Weighted_periodic_alpha_complex_3d::Point_3; + using Point = typename Weighted_periodic_alpha_complex_3d::Bare_point_3; using Weighted_point = typename Weighted_periodic_alpha_complex_3d::Weighted_point_3; std::vector points; diff --git a/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp b/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp index 0e359a27..7bd35cc6 100644 --- a/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp +++ b/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp @@ -8,7 +8,7 @@ #include // for numeric limits using Alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d; -using Point = Alpha_complex_3d::Point_3; +using Point = Alpha_complex_3d::Bare_point_3; using Vector_of_points = std::vector; int main(int argc, char **argv) { diff --git a/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp b/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp index ac11b68c..fcf80802 100644 --- a/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp +++ b/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp @@ -10,7 +10,7 @@ // Complexity = FAST, weighted = true, periodic = false using Weighted_alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d; -using Point = Weighted_alpha_complex_3d::Point_3; +using Bare_point = Weighted_alpha_complex_3d::Bare_point_3; using Weighted_point = Weighted_alpha_complex_3d::Weighted_point_3; int main(int argc, char **argv) { @@ -18,11 +18,11 @@ int main(int argc, char **argv) { // Init of a list of points and weights from a small molecule // ---------------------------------------------------------------------------- std::vector weighted_points; - weighted_points.push_back(Weighted_point(Point(1, -1, -1), 4.)); - weighted_points.push_back(Weighted_point(Point(-1, 1, -1), 4.)); - weighted_points.push_back(Weighted_point(Point(-1, -1, 1), 4.)); - weighted_points.push_back(Weighted_point(Point(1, 1, 1), 4.)); - weighted_points.push_back(Weighted_point(Point(2, 2, 2), 1.)); + weighted_points.push_back(Weighted_point(Bare_point(1, -1, -1), 4.)); + weighted_points.push_back(Weighted_point(Bare_point(-1, 1, -1), 4.)); + weighted_points.push_back(Weighted_point(Bare_point(-1, -1, 1), 4.)); + weighted_points.push_back(Weighted_point(Bare_point(1, 1, 1), 4.)); + weighted_points.push_back(Weighted_point(Bare_point(2, 2, 2), 1.)); // ---------------------------------------------------------------------------- // Init of an alpha complex from the list of points diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index 13ebb9c1..c36bed1f 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -43,7 +43,7 @@ #include #include #include -#include +#include // for std::size_t #include // for std::unique_ptr #include // for std::conditional and std::enable_if #include // for numeric_limits<> @@ -225,23 +225,23 @@ class Alpha_complex_3d { * Must be compatible with double. */ using FT = typename Alpha_shape_3::FT; - /** \brief Gives public access to the Point_3 type. Here is a Point_3 constructor example: + /** \brief Gives public access to the Bare_point_3 (bare aka. unweighed) type. + * Here is a Bare_point_3 constructor example: \code{.cpp} using Alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d; // x0 = 1., y0 = -1.1, z0 = -1.. -Alpha_complex_3d::Point_3 p0(1., -1.1, -1.); +Alpha_complex_3d::Bare_point_3 p0(1., -1.1, -1.); \endcode * */ - using Point_3 = typename Kernel::Point_3; + using Bare_point_3 = typename Kernel::Point_3; /** \brief Gives public access to the Weighted_point_3 type. A Weighted point can be constructed as follows: \code{.cpp} -using Weighted_alpha_complex_3d = - Gudhi::alpha_complex::Alpha_complex_3d; +using Weighted_alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d; // x0 = 1., y0 = -1.1, z0 = -1., weight = 4. -Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point_3(1., -1.1, -1.), 4.); +Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_point_3(1., -1.1, -1.), 4.); \endcode * * Note: This type is defined to void if Alpha complex is not weighted. @@ -249,6 +249,11 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * */ using Weighted_point_3 = typename Triangulation_3::Weighted_point_3; + /** \brief `Alpha_complex_3d::Point_3` type is either a `Alpha_complex_3d::Bare_point_3` (Weighted = false) or a + * `Alpha_complex_3d::Weighted_point_3` (Weighted = true). + */ + using Point_3 = typename std::conditional::type; + private: using Dispatch = CGAL::Dispatch_output_iterator, @@ -264,13 +269,13 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point public: /** \brief Alpha_complex constructor from a list of points. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3` or + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3` or * `Alpha_complex_3d::Weighted_point_3`. * * @pre Available if Alpha_complex_3d is not Periodic. * * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a - * `Alpha_complex_3d::Point_3` or a `Alpha_complex_3d::Weighted_point_3`. + * `Alpha_complex_3d::Bare_point_3` or a `Alpha_complex_3d::Weighted_point_3`. */ template Alpha_complex_3d(const InputPointRange& points) { @@ -284,13 +289,13 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * * @exception std::invalid_argument In debug mode, if points and weights do not have the same size. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3`. * @param[in] weights Range of weights on points. Weights shall be in double. * * @pre Available if Alpha_complex_3d is Weighted and not Periodic. * * The type InputPointRange must be a range for which std::begin and - * std::end return input iterators on a `Alpha_complex_3d::Point_3`. + * std::end return input iterators on a `Alpha_complex_3d::Bare_point_3`. * The type WeightRange must be a range for which std::begin and * std::end return an input iterator on a double. */ @@ -318,7 +323,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * * @exception std::invalid_argument In debug mode, if the size of the cuboid in every directions is not the same. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3` or + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3` or * `Alpha_complex_3d::Weighted_point_3`. * @param[in] x_min Iso-oriented cuboid x_min. * @param[in] y_min Iso-oriented cuboid y_min. @@ -330,7 +335,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * @pre Available if Alpha_complex_3d is Periodic. * * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a - * `Alpha_complex_3d::Point_3` or a `Alpha_complex_3d::Weighted_point_3`. + * `Alpha_complex_3d::Bare_point_3` or a `Alpha_complex_3d::Weighted_point_3`. * * @note In weighted version, please check weights are greater than zero, and lower than 1/64*cuboid length * squared. @@ -366,7 +371,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * @exception std::invalid_argument In debug mode, if a weight is negative, zero, or greater than 1/64*cuboid length * squared. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3`. * @param[in] weights Range of weights on points. Weights shall be in double. * @param[in] x_min Iso-oriented cuboid x_min. * @param[in] y_min Iso-oriented cuboid y_min. @@ -378,7 +383,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * @pre Available if Alpha_complex_3d is Weighted and Periodic. * * The type InputPointRange must be a range for which std::begin and - * std::end return input iterators on a `Alpha_complex_3d::Point_3`. + * std::end return input iterators on a `Alpha_complex_3d::Bare_point_3`. * The type WeightRange must be a range for which std::begin and * std::end return an input iterator on a double. * The type of x_min, y_min, z_min, x_max, y_max and z_max must be a double. @@ -452,9 +457,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point return false; // ----- >> } - // using Filtration_value = typename SimplicialComplexForAlpha3d::Filtration_value; using Complex_vertex_handle = typename SimplicialComplexForAlpha3d::Vertex_handle; - using Alpha_shape_simplex_tree_map = std::unordered_map; using Simplex_tree_vector_vertex = std::vector; #ifdef DEBUG_TRACES @@ -474,7 +477,6 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point std::cout << "filtration_with_alpha_values returns : " << objects.size() << " objects" << std::endl; #endif // DEBUG_TRACES - Alpha_shape_simplex_tree_map map_cgal_simplex_tree; using Alpha_value_iterator = typename std::vector::const_iterator; Alpha_value_iterator alpha_value_iterator = alpha_values.begin(); for (auto object_iterator : objects) { @@ -484,7 +486,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point if (const Cell_handle* cell = CGAL::object_cast(&object_iterator)) { for (auto i = 0; i < 4; i++) { #ifdef DEBUG_TRACES - std::cout << "from cell[" << i << "]=" << (*cell)->vertex(i)->point() << std::endl; + std::cout << "from cell[" << i << "] - Point coordinates (" << (*cell)->vertex(i)->point() << ")" + << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*cell)->vertex(i)); } @@ -495,7 +498,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point for (auto i = 0; i < 4; i++) { if ((*facet).second != i) { #ifdef DEBUG_TRACES - std::cout << "from facet=[" << i << "]" << (*facet).first->vertex(i)->point() << std::endl; + std::cout << "from facet=[" << i << "] - Point coordinates (" << (*facet).first->vertex(i)->point() << ")" + << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*facet).first->vertex(i)); } @@ -506,7 +510,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point } else if (const Edge* edge = CGAL::object_cast(&object_iterator)) { for (auto i : {(*edge).second, (*edge).third}) { #ifdef DEBUG_TRACES - std::cout << "from edge[" << i << "]=" << (*edge).first->vertex(i)->point() << std::endl; + std::cout << "from edge[" << i << "] - Point coordinates (" << (*edge).first->vertex(i)->point() << ")" + << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*edge).first->vertex(i)); } @@ -516,7 +521,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point } else if (const Alpha_vertex_handle* vertex = CGAL::object_cast(&object_iterator)) { #ifdef DEBUG_TRACES count_vertices++; - std::cout << "from vertex=" << (*vertex)->point() << std::endl; + std::cout << "from vertex - Point coordinates (" << (*vertex)->point() << ")" << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*vertex)); } @@ -528,7 +533,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point // alpha shape not found Complex_vertex_handle vertex = map_cgal_simplex_tree.size(); #ifdef DEBUG_TRACES - std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] not found - insert " << vertex << std::endl; + std::cout << "Point (" << the_alpha_shape_vertex->point() << ") not found - insert new vertex id " << vertex + << std::endl; #endif // DEBUG_TRACES the_simplex.push_back(vertex); map_cgal_simplex_tree.emplace(the_alpha_shape_vertex, vertex); @@ -536,7 +542,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point // alpha shape found Complex_vertex_handle vertex = the_map_iterator->second; #ifdef DEBUG_TRACES - std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] found in " << vertex << std::endl; + std::cout << "Point (" << the_alpha_shape_vertex->point() << ") found as vertex id " << vertex << std::endl; #endif // DEBUG_TRACES the_simplex.push_back(vertex); } @@ -567,9 +573,32 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point return true; } + /** \brief get_point returns the point corresponding to the vertex given as parameter. + * + * @param[in] vertex Vertex handle of the point to retrieve. + * @return The point found. + * @exception std::out_of_range In case vertex is not found (cf. std::vector::at). + */ + const Point_3& get_point(std::size_t vertex) { + if (map_cgal_simplex_tree.size() != cgal_vertex_iterator_vector.size()) { + cgal_vertex_iterator_vector.resize(map_cgal_simplex_tree.size()); + for (auto map_iterator = map_cgal_simplex_tree.begin(); map_iterator != map_cgal_simplex_tree.end(); map_iterator++) { + cgal_vertex_iterator_vector[map_iterator->second] = map_iterator->first; + } + + } + auto cgal_vertex_iterator = cgal_vertex_iterator_vector.at(vertex); + return cgal_vertex_iterator->point(); + } + private: // use of a unique_ptr on cgal Alpha_shape_3, as copy and default constructor is not available - no need to be freed std::unique_ptr alpha_shape_3_ptr_; + + // Map type to switch from CGAL vertex iterator to simplex tree vertex handle. + std::unordered_map map_cgal_simplex_tree; + // Vector type to switch from simplex tree vertex handle to CGAL vertex iterator. + std::vector cgal_vertex_iterator_vector; }; } // namespace alpha_complex diff --git a/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp index 1102838a..cd698a27 100644 --- a/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp +++ b/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp @@ -56,21 +56,52 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_3d_from_points) { // ----------------- std::cout << "Fast alpha complex 3d" << std::endl; - Fast_alpha_complex_3d alpha_complex(get_points()); + std::vector points = get_points(); + Fast_alpha_complex_3d alpha_complex(points); Gudhi::Simplex_tree<> stree; alpha_complex.create_complex(stree); + for (std::size_t index = 0; index < points.size(); index++) { + bool found = false; + for (auto point : points) { + if (point == alpha_complex.get_point(index)) { + found = true; + break; + } + } + // Check all points from alpha complex are found in the input point cloud + BOOST_CHECK(found); + } + // Exception if we go out of range + BOOST_CHECK_THROW(alpha_complex.get_point(points.size()), std::out_of_range); + // ----------------- // Exact version // ----------------- std::cout << "Exact alpha complex 3d" << std::endl; - Exact_alpha_complex_3d exact_alpha_complex(get_points()); + std::vector exact_points = get_points(); + Exact_alpha_complex_3d exact_alpha_complex(exact_points); Gudhi::Simplex_tree<> exact_stree; exact_alpha_complex.create_complex(exact_stree); + for (std::size_t index = 0; index < exact_points.size(); index++) { + bool found = false; + Exact_alpha_complex_3d::Bare_point_3 ap = exact_alpha_complex.get_point(index); + for (auto point : points) { + if ((point.x() == ap.x()) && (point.y() == ap.y()) && (point.z() == ap.z())) { + found = true; + break; + } + } + // Check all points from alpha complex are found in the input point cloud + BOOST_CHECK(found); + } + // Exception if we go out of range + BOOST_CHECK_THROW(exact_alpha_complex.get_point(exact_points.size()), std::out_of_range); + // --------------------- // Compare both versions // --------------------- @@ -110,11 +141,27 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_3d_from_points) { // ----------------- std::cout << "Safe alpha complex 3d" << std::endl; - Safe_alpha_complex_3d safe_alpha_complex(get_points()); + std::vector safe_points = get_points(); + Safe_alpha_complex_3d safe_alpha_complex(safe_points); Gudhi::Simplex_tree<> safe_stree; safe_alpha_complex.create_complex(safe_stree); + for (std::size_t index = 0; index < safe_points.size(); index++) { + bool found = false; + Safe_alpha_complex_3d::Bare_point_3 ap = safe_alpha_complex.get_point(index); + for (auto point : points) { + if ((point.x() == ap.x()) && (point.y() == ap.y()) && (point.z() == ap.z())) { + found = true; + break; + } + } + // Check all points from alpha complex are found in the input point cloud + BOOST_CHECK(found); + } + // Exception if we go out of range + BOOST_CHECK_THROW(safe_alpha_complex.get_point(safe_points.size()), std::out_of_range); + // --------------------- // Compare both versions // --------------------- diff --git a/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp index ac3791a4..731763fa 100644 --- a/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp +++ b/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp @@ -44,11 +44,11 @@ typedef boost::mpl::list p_points; + using Bare_point_3 = typename Periodic_alpha_complex_3d::Bare_point_3; + std::vector p_points; // Not important, this is not what we want to check - p_points.push_back(Point_3(0.0, 0.0, 0.0)); + p_points.push_back(Bare_point_3(0.0, 0.0, 0.0)); std::cout << "Check exception throw in debug mode" << std::endl; // Check it throws an exception when the cuboid is not iso @@ -73,13 +73,13 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_periodic) { // --------------------- std::cout << "Fast periodic alpha complex 3d" << std::endl; - using Creator = CGAL::Creator_uniform_3; + using Creator = CGAL::Creator_uniform_3; CGAL::Random random(7); - CGAL::Random_points_in_cube_3 in_cube(1, random); - std::vector p_points; + CGAL::Random_points_in_cube_3 in_cube(1, random); + std::vector p_points; for (int i = 0; i < 50; i++) { - Fast_periodic_alpha_complex_3d::Point_3 p = *in_cube++; + Fast_periodic_alpha_complex_3d::Bare_point_3 p = *in_cube++; p_points.push_back(p); } @@ -88,15 +88,30 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_periodic) { Gudhi::Simplex_tree<> stree; periodic_alpha_complex.create_complex(stree); + for (std::size_t index = 0; index < p_points.size(); index++) { + bool found = false; + Fast_periodic_alpha_complex_3d::Bare_point_3 ap = periodic_alpha_complex.get_point(index); + for (auto point : p_points) { + if ((point.x() == ap.x()) && (point.y() == ap.y()) && (point.z() == ap.z())) { + found = true; + break; + } + } + // Check all points from alpha complex are found in the input point cloud + BOOST_CHECK(found); + } + // Exception if we go out of range + BOOST_CHECK_THROW(periodic_alpha_complex.get_point(p_points.size()), std::out_of_range); + // ---------------------- // Exact periodic version // ---------------------- std::cout << "Exact periodic alpha complex 3d" << std::endl; - std::vector e_p_points; + std::vector e_p_points; for (auto p : p_points) { - e_p_points.push_back(Exact_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2])); + e_p_points.push_back(Exact_periodic_alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); } Exact_periodic_alpha_complex_3d exact_alpha_complex(e_p_points, -1., -1., -1., 1., 1., 1.); @@ -142,10 +157,10 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_periodic) { // ---------------------- std::cout << "Safe periodic alpha complex 3d" << std::endl; - std::vector s_p_points; + std::vector s_p_points; for (auto p : p_points) { - s_p_points.push_back(Safe_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2])); + s_p_points.push_back(Safe_periodic_alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); } Safe_periodic_alpha_complex_3d safe_alpha_complex(s_p_points, -1., -1., -1., 1., 1., 1.); diff --git a/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp index 44deb930..8035f6e8 100644 --- a/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp +++ b/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp @@ -43,14 +43,14 @@ typedef boost::mpl::list w_points; - w_points.push_back(Point_3(0.0, 0.0, 0.0)); - w_points.push_back(Point_3(0.0, 0.0, 0.2)); - w_points.push_back(Point_3(0.2, 0.0, 0.2)); - // w_points.push_back(Point_3(0.6, 0.6, 0.0)); - // w_points.push_back(Point_3(0.8, 0.8, 0.2)); - // w_points.push_back(Point_3(0.2, 0.8, 0.6)); + using Bare_point_3 = typename Weighted_alpha_complex_3d::Bare_point_3; + std::vector w_points; + w_points.push_back(Bare_point_3(0.0, 0.0, 0.0)); + w_points.push_back(Bare_point_3(0.0, 0.0, 0.2)); + w_points.push_back(Bare_point_3(0.2, 0.0, 0.2)); + // w_points.push_back(Bare_point_3(0.6, 0.6, 0.0)); + // w_points.push_back(Bare_point_3(0.8, 0.8, 0.2)); + // w_points.push_back(Bare_point_3(0.2, 0.8, 0.6)); // weights size is different from w_points size to make weighted Alpha_complex_3d throw in debug mode std::vector weights = {0.01, 0.005, 0.006, 0.01, 0.009, 0.001}; @@ -62,14 +62,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted_throw, Weighted_alpha_compl BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted, Weighted_alpha_complex_3d, weighted_variants_type_list) { std::cout << "Weighted alpha complex 3d from points and weights" << std::endl; - using Point_3 = typename Weighted_alpha_complex_3d::Point_3; - std::vector w_points; - w_points.push_back(Point_3(0.0, 0.0, 0.0)); - w_points.push_back(Point_3(0.0, 0.0, 0.2)); - w_points.push_back(Point_3(0.2, 0.0, 0.2)); - w_points.push_back(Point_3(0.6, 0.6, 0.0)); - w_points.push_back(Point_3(0.8, 0.8, 0.2)); - w_points.push_back(Point_3(0.2, 0.8, 0.6)); + using Bare_point_3 = typename Weighted_alpha_complex_3d::Bare_point_3; + std::vector w_points; + w_points.push_back(Bare_point_3(0.0, 0.0, 0.0)); + w_points.push_back(Bare_point_3(0.0, 0.0, 0.2)); + w_points.push_back(Bare_point_3(0.2, 0.0, 0.2)); + w_points.push_back(Bare_point_3(0.6, 0.6, 0.0)); + w_points.push_back(Bare_point_3(0.8, 0.8, 0.2)); + w_points.push_back(Bare_point_3(0.2, 0.8, 0.6)); // weights size is different from w_points size to make weighted Alpha_complex_3d throw in debug mode std::vector weights = {0.01, 0.005, 0.006, 0.01, 0.009, 0.001}; @@ -91,6 +91,24 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted, Weighted_alpha_complex_3d, Gudhi::Simplex_tree<> stree_bis; alpha_complex_w_p.create_complex(stree_bis); + for (std::size_t index = 0; index < weighted_points.size(); index++) { + bool found = false; + Weighted_point_3 awp = alpha_complex_w_p.get_point(index); + for (auto weighted_point : weighted_points) { + if ((weighted_point.weight() == awp.weight()) && + (weighted_point.x() == awp.x()) && + (weighted_point.y() == awp.y()) && + (weighted_point.z() == awp.z())) { + found = true; + break; + } + } + // Check all points from alpha complex are found in the input point cloud + BOOST_CHECK(found); + } + // Exception if we go out of range + BOOST_CHECK_THROW(alpha_complex_w_p.get_point(weighted_points.size()), std::out_of_range); + // --------------------- // Compare both versions // --------------------- diff --git a/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp index 670c7799..b09e92d5 100644 --- a/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp +++ b/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp @@ -47,13 +47,13 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted_periodic_throw, Weighted_pe wp_variants_type_list) { std::cout << "Weighted periodic alpha complex 3d exception throw" << std::endl; - using Creator = CGAL::Creator_uniform_3; + using Creator = CGAL::Creator_uniform_3; CGAL::Random random(7); - CGAL::Random_points_in_cube_3 in_cube(1, random); - std::vector wp_points; + CGAL::Random_points_in_cube_3 in_cube(1, random); + std::vector wp_points; for (int i = 0; i < 50; i++) { - typename Weighted_periodic_alpha_complex_3d::Point_3 p = *in_cube++; + typename Weighted_periodic_alpha_complex_3d::Bare_point_3 p = *in_cube++; wp_points.push_back(p); } std::vector p_weights; @@ -117,13 +117,13 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_weighted_periodic) { // --------------------- std::cout << "Fast weighted periodic alpha complex 3d" << std::endl; - using Creator = CGAL::Creator_uniform_3; + using Creator = CGAL::Creator_uniform_3; CGAL::Random random(7); - CGAL::Random_points_in_cube_3 in_cube(1, random); - std::vector p_points; + CGAL::Random_points_in_cube_3 in_cube(1, random); + std::vector p_points; for (int i = 0; i < 50; i++) { - Fast_weighted_periodic_alpha_complex_3d::Point_3 p = *in_cube++; + Fast_weighted_periodic_alpha_complex_3d::Bare_point_3 p = *in_cube++; p_points.push_back(p); } std::vector p_weights; @@ -142,10 +142,10 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_weighted_periodic) { // ---------------------- std::cout << "Exact weighted periodic alpha complex 3d" << std::endl; - std::vector e_p_points; + std::vector e_p_points; for (auto p : p_points) { - e_p_points.push_back(Exact_weighted_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2])); + e_p_points.push_back(Exact_weighted_periodic_alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); } Exact_weighted_periodic_alpha_complex_3d exact_alpha_complex(e_p_points, p_weights, -1., -1., -1., 1., 1., 1.); @@ -191,10 +191,10 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_weighted_periodic) { // ---------------------- std::cout << "Safe weighted periodic alpha complex 3d" << std::endl; - std::vector s_p_points; + std::vector s_p_points; for (auto p : p_points) { - s_p_points.push_back(Safe_weighted_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2])); + s_p_points.push_back(Safe_weighted_periodic_alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); } Safe_weighted_periodic_alpha_complex_3d safe_alpha_complex(s_p_points, p_weights, -1., -1., -1., 1., 1., 1.); diff --git a/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp b/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp index 2272576e..929fc2e8 100644 --- a/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp +++ b/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp @@ -62,9 +62,9 @@ bool read_cuboid_file(const std::string &cuboid_file, double &x_min, double &y_m } template -std::vector read_off(const std::string &off_file_points) { +std::vector read_off(const std::string &off_file_points) { // Read the OFF file (input file name given as parameter) and triangulate points - Gudhi::Points_3D_off_reader off_reader(off_file_points); + Gudhi::Points_3D_off_reader off_reader(off_file_points); // Check the read operation was correct if (!off_reader.is_valid()) { std::cerr << "Unable to read OFF file " << off_file_points << std::endl; -- cgit v1.2.3 From 1e42426b292f4bdc40044eedda59bbb51e2ca851 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 31 Oct 2019 15:52:05 +0100 Subject: Fix #98 : set conda link to the correct one (not licensing link) --- src/python/doc/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 77d9e8b3..5369efb0 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -8,7 +8,7 @@ Installation Conda ***** The easiest way to install the Python version of GUDHI is using -`conda `_. +`conda `_. Compiling ********* -- cgit v1.2.3 From 3e18343985f7d4439fb6acb879547e8fa11369f9 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 1 Nov 2019 14:52:44 +0100 Subject: Use documented CGAL interfaces for Epick_d (or Epeck_d) --- src/python/include/Alpha_complex_interface.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/python/include/Alpha_complex_interface.h b/src/python/include/Alpha_complex_interface.h index b3553d32..96353cc4 100644 --- a/src/python/include/Alpha_complex_interface.h +++ b/src/python/include/Alpha_complex_interface.h @@ -15,6 +15,8 @@ #include #include +#include + #include "Simplex_tree_interface.h" #include @@ -31,7 +33,10 @@ class Alpha_complex_interface { public: Alpha_complex_interface(const std::vector>& points) { - alpha_complex_ = new Alpha_complex(points); + auto mkpt = [](std::vector const& vec){ + return Point_d(vec.size(), vec.begin(), vec.end()); + }; + alpha_complex_ = new Alpha_complex(boost::adaptors::transform(points, mkpt)); } Alpha_complex_interface(const std::string& off_file_name, bool from_file = true) { @@ -45,9 +50,9 @@ class Alpha_complex_interface { std::vector get_point(int vh) { std::vector vd; try { - Point_d ph = alpha_complex_->get_point(vh); + Point_d const& ph = alpha_complex_->get_point(vh); for (auto coord = ph.cartesian_begin(); coord < ph.cartesian_end(); coord++) - vd.push_back(*coord); + vd.push_back(CGAL::to_double(*coord)); } catch (std::out_of_range const&) { // std::out_of_range is thrown in case not found. Other exceptions must be re-thrown } -- cgit v1.2.3 From f670d1e58ff94fd724c41ff34871b57f6ac95cc0 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 4 Nov 2019 10:51:28 +0100 Subject: Use Point_3 instead of Bare_point_3 in non-weighted cases --- src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp | 6 +++--- src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp b/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp index e84f5229..99ad94b9 100644 --- a/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp +++ b/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp @@ -71,10 +71,10 @@ void benchmark_points_on_torus_3D(const std::string& msg) { for (int nb_points = 1000; nb_points <= 125000; nb_points *= 5) { std::cout << " Alpha complex 3d on torus with " << nb_points << " points." << std::endl; std::vector points_on_torus = Gudhi::generate_points_on_torus_3D(nb_points, 1.0, 0.5); - std::vector points; + std::vector points; for (auto p : points_on_torus) { - points.push_back(typename Alpha_complex_3d::Bare_point_3(p[0], p[1], p[2])); + points.push_back(typename Alpha_complex_3d::Point_3(p[0], p[1], p[2])); } Gudhi::Clock ac_create_clock(" benchmark_points_on_torus_3D - Alpha complex 3d creation"); @@ -158,7 +158,7 @@ void benchmark_periodic_points(const std::string& msg) { for (double nb_points = 10.; nb_points <= 40.; nb_points += 10.) { std::cout << " Periodic alpha complex 3d with " << nb_points * nb_points * nb_points << " points." << std::endl; - using Point = typename Periodic_alpha_complex_3d::Bare_point_3; + using Point = typename Periodic_alpha_complex_3d::Point_3; std::vector points; for (double i = 0; i < nb_points; i++) { diff --git a/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp b/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp index 7bd35cc6..0e359a27 100644 --- a/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp +++ b/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp @@ -8,7 +8,7 @@ #include // for numeric limits using Alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d; -using Point = Alpha_complex_3d::Bare_point_3; +using Point = Alpha_complex_3d::Point_3; using Vector_of_points = std::vector; int main(int argc, char **argv) { -- cgit v1.2.3 From c7d99937affeeac7ef1de05baa42d0392c8d5dc3 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 4 Nov 2019 14:33:50 +0100 Subject: Clarification : Points must be in Bare_point_3 or in Weighted_point_3 *means* points must be in Point_3 --- src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index c36bed1f..652a40a4 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -269,13 +269,12 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_ public: /** \brief Alpha_complex constructor from a list of points. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3` or - * `Alpha_complex_3d::Weighted_point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. * * @pre Available if Alpha_complex_3d is not Periodic. * * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a - * `Alpha_complex_3d::Bare_point_3` or a `Alpha_complex_3d::Weighted_point_3`. + * `Alpha_complex_3d::Point_3`. */ template Alpha_complex_3d(const InputPointRange& points) { @@ -323,8 +322,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_ * * @exception std::invalid_argument In debug mode, if the size of the cuboid in every directions is not the same. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3` or - * `Alpha_complex_3d::Weighted_point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. * @param[in] x_min Iso-oriented cuboid x_min. * @param[in] y_min Iso-oriented cuboid y_min. * @param[in] z_min Iso-oriented cuboid z_min. @@ -335,7 +333,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_ * @pre Available if Alpha_complex_3d is Periodic. * * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a - * `Alpha_complex_3d::Bare_point_3` or a `Alpha_complex_3d::Weighted_point_3`. + * `Alpha_complex_3d::Point_3`. * * @note In weighted version, please check weights are greater than zero, and lower than 1/64*cuboid length * squared. -- cgit v1.2.3 From b3043e49f09551187e848e0aacf88d1cf5e271e4 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 4 Nov 2019 15:09:28 +0100 Subject: Use range-for loop --- src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index 652a40a4..cb9cb926 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -580,8 +580,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_ const Point_3& get_point(std::size_t vertex) { if (map_cgal_simplex_tree.size() != cgal_vertex_iterator_vector.size()) { cgal_vertex_iterator_vector.resize(map_cgal_simplex_tree.size()); - for (auto map_iterator = map_cgal_simplex_tree.begin(); map_iterator != map_cgal_simplex_tree.end(); map_iterator++) { - cgal_vertex_iterator_vector[map_iterator->second] = map_iterator->first; + for (auto map_iterator : map_cgal_simplex_tree) { + cgal_vertex_iterator_vector[map_iterator.second] = map_iterator.first; } } -- cgit v1.2.3 From 7eb50adc342a5d2757cd209b3a8f2f297a4ad2fe Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 4 Nov 2019 11:27:19 -0500 Subject: fixes of Marc's comments --- src/python/example/ex_diagrams.py | 57 +++++++++++++++++++++++--------- src/python/gudhi/sktda/kernel_methods.py | 8 ++--- src/python/gudhi/sktda/metrics.py | 2 +- src/python/gudhi/sktda/preprocessing.py | 16 +++++---- src/python/gudhi/sktda/vector_methods.py | 8 ++--- 5 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py index a3efbac9..f12304bd 100755 --- a/src/python/example/ex_diagrams.py +++ b/src/python/example/ex_diagrams.py @@ -3,25 +3,33 @@ import matplotlib.pyplot as plt import numpy as np from sklearn.kernel_approximation import RBFSampler +from sklearn.preprocessing import MinMaxScaler -from gudhi.sktda import Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ +from gudhi.sktda import DiagramSelector, Clamping, Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ TopologicalVector, DiagramScaler, BirthPersistenceTransform,\ - PersistenceImage, PersistenceWeightedGaussianKernel,\ + PersistenceImage, PersistenceWeightedGaussianKernel, Entropy, \ PersistenceScaleSpaceKernel, SlicedWassersteinDistance,\ SlicedWassersteinKernel, BottleneckDistance, PersistenceFisherKernel -D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.]]) +D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.], [0., np.inf], [5., np.inf]]) +diags = [D] + +diags = DiagramSelector(use=True, point_type="finite").fit_transform(diags) +diags = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags) +diags = DiagramScaler(use=True, scalers=[([1], Clamping(limit=.9))]).fit_transform(diags) + +D = diags[0] plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,10.],[0.,10.]) +plt.plot([0.,1.],[0.,1.]) +plt.title("Test Persistence Diagram for vector methods") plt.show() -diags = [D] - -LS = Landscape(resolution = 1000) +LS = Landscape(resolution=1000) L = LS.fit_transform(diags) plt.plot(L[0][:1000]) plt.plot(L[0][1000:2000]) plt.plot(L[0][2000:3000]) +plt.title("Landscape") plt.show() def pow(n): @@ -30,11 +38,13 @@ def pow(n): SH = Silhouette(resolution=1000, weight=pow(2)) sh = SH.fit_transform(diags) plt.plot(sh[0]) +plt.title("Silhouette") plt.show() BC = BettiCurve(resolution=1000) bc = BC.fit_transform(diags) plt.plot(bc[0]) +plt.title("Betti Curve") plt.show() CP = ComplexPolynomial(threshold=-1, F="T") @@ -45,20 +55,35 @@ TV = TopologicalVector(threshold=-1) tv = TV.fit_transform(diags) print("Topological vector is " + str(tv[0,:])) -#diagsT = DiagramPreprocessor(use=True, scalers=[([0,1], BirthPersistenceTransform())]).fit_transform(diags) -#PI = PersistenceImage(bandwidth=1., weight=lambda x: x[1], im_range=[0,10,0,10], resolution=[100,100]) -#pi = PI.fit_transform(diagsT) -#plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) -#plt.show() +PI = PersistenceImage(bandwidth=.1, weight=lambda x: x[1], im_range=[0,1,0,1], resolution=[100,100]) +pi = PI.fit_transform(diags) +plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) +plt.title("Persistence Image") +plt.show() -plt.scatter(D[:,0],D[:,1]) -D = np.array([[1.,5.],[3.,6.],[2.,7.]]) -plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,10.],[0.,10.]) +ET = Entropy(mode="scalar") +et = ET.fit_transform(diags) +print("Entropy statistic is " + str(et[0,:])) + +ET = Entropy(mode="vector", normalized=False) +et = ET.fit_transform(diags) +plt.plot(et[0]) +plt.title("Entropy function") plt.show() +D = np.array([[1.,5.],[3.,6.],[2.,7.]]) diags2 = [D] +diags2 = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags2) + +D = diags[0] +plt.scatter(D[:,0],D[:,1]) +D = diags2[0] +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,1.],[0.,1.]) +plt.title("Test Persistence Diagrams for kernel methods") +plt.show() + def arctan(C,p): return lambda x: C*np.arctan(np.power(x[1], p)) diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index d90bf164..b49bdf60 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -18,11 +18,11 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): """ def __init__(self, num_directions=10, bandwidth=1.0): """ - Constructor for the SlicedWassersteinDistance class. + Constructor for the SlicedWassersteinKernel class. Attributes: bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + num_directions (int): number of lines evenly sampled on [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. """ self.bandwidth = bandwidth self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) @@ -82,7 +82,7 @@ class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): def transform(self, X): """ - Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + Compute all persistence weighted Gaussian kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. @@ -118,7 +118,7 @@ class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): """ - This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. + This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, with negative weight, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. """ def __init__(self, bandwidth=1., kernel_approx=None): """ diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index 8092f7af..816441b6 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -34,7 +34,7 @@ class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): def fit(self, X, y=None): """ - Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in a numpy array called **diagrams**. + Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in numpy arrays, called **diagrams_** and **approx_diag_**. Parameters: X (list of n x 2 numpy arrays): input persistence diagrams. diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 3c625053..784e300f 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -43,8 +43,9 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): """ Xfit = [] for diag in X: - new_diag = np.empty(diag.shape) - np.copyto(new_diag, diag) + #new_diag = np.empty(diag.shape) + #np.copyto(new_diag, diag) + new_diag = np.copy(diag) new_diag[:,1] = new_diag[:,1] - new_diag[:,0] Xfit.append(new_diag) return Xfit @@ -82,7 +83,8 @@ class Clamping(BaseEstimator, TransformerMixin): Returns: Xfit (numpy array of size n): output list of values. """ - Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) + Xfit = np.minimum(X, self.limit) + #Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) return Xfit class DiagramScaler(BaseEstimator, TransformerMixin): @@ -91,7 +93,7 @@ class DiagramScaler(BaseEstimator, TransformerMixin): """ def __init__(self, use=False, scalers=[]): """ - Constructor for the DiagramPreprocessor class. + Constructor for the DiagramScaler class. Attributes: use (bool): whether to use the class or not (default False). @@ -102,7 +104,7 @@ class DiagramScaler(BaseEstimator, TransformerMixin): def fit(self, X, y=None): """ - Fit the DiagramPreprocessor class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. + Fit the DiagramScaler class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. Parameters: X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. @@ -119,7 +121,7 @@ class DiagramScaler(BaseEstimator, TransformerMixin): def transform(self, X): """ - Apply the DiagramPreprocessor function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. + Apply the DiagramScaler function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. Parameters: X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. @@ -293,7 +295,7 @@ class DiagramSelector(BaseEstimator, TransformerMixin): if self.point_type == "finite": Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] else: - Xfit = [ diag[diag[:,1] == self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] + Xfit = [ diag[diag[:,1] >= self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] else: Xfit = X return Xfit diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index 3862f815..d767a952 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -69,7 +69,7 @@ class PersistenceImage(BaseEstimator, TransformerMixin): x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) - image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(self.bandwidth*np.sqrt(2*np.pi)), 1) + image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(np.square(self.bandwidth)*2*np.pi), 1) Xfit.append(image.flatten()[np.newaxis,:]) @@ -299,7 +299,7 @@ class BettiCurve(BaseEstimator, TransformerMixin): class Entropy(BaseEstimator, TransformerMixin): """ - This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. + This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. Note that a previous implementation was contributed by Manuel Soriano-Trigueros. """ def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): """ @@ -376,7 +376,7 @@ class TopologicalVector(BaseEstimator, TransformerMixin): Constructor for the TopologicalVector class. Attributes: - threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. + threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If , this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. """ self.threshold = threshold @@ -430,7 +430,7 @@ class ComplexPolynomial(BaseEstimator, TransformerMixin): Attributes: F (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). - threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. + threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients, i.e. the number of coefficients corresponding to the largest degree terms of the polynomial. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. """ self.threshold, self.F = threshold, F -- cgit v1.2.3 From 73785832fd7df6a300985d1edf3a386b64e0ce19 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 4 Nov 2019 22:44:27 +0100 Subject: Epeck is now the default value. Epick and Epeck in doc. User warning on ctor if Epeck with CGAL < 5.X --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 8919cdb9..0cb4accb 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -20,7 +20,8 @@ #include // isnan, fmax #include -#include +#include // For EXACT or SAFE version +#include // For FAST version #include #include // for CGAL::Identity_property_map #include @@ -39,17 +40,20 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Alpha_complex is only available for CGAL >= 4.11 #endif #if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL +# error Alpha_complex is only available for Eigen3 >= 3.1.0 installed with CGAL #endif namespace Gudhi { namespace alpha_complex { +template struct Is_Epick_D { static const bool value = false; }; +template struct Is_Epick_D> { static const bool value = true; }; + /** * \class Alpha_complex Alpha_complex.h gudhi/Alpha_complex.h * \brief Alpha complex data structure. @@ -63,17 +67,20 @@ namespace alpha_complex { * * Please refer to \ref alpha_complex for examples. * - * The complex is a template class requiring an Epick_d CGAL::Epeck_d, + * or an CGAL::Epick_d dD Geometry Kernel * \cite cgal:s-gkd-15b from CGAL as template, default value is CGAL::Epick_d + * href="https://doc.cgal.org/latest/Kernel_d/structCGAL_1_1Epeck__d.html">CGAL::Epeck_d * < * CGAL::Dynamic_dimension_tag > * * \remark When Alpha_complex is constructed with an infinite value of alpha, the complex is a Delaunay complex. * */ -template> +template> class Alpha_complex { public: // Add an int in TDS to save point index in the structure @@ -184,6 +191,12 @@ class Alpha_complex { private: template void init_from_range(const InputPointRange& points) { + #if CGAL_VERSION_NR < 1050000000 + if (Is_Epick_D::value) + std::cerr << "It is strongly advised to use a CGAL version from 5.0 for performance reasons." + << "Your CGAL version is " << CGAL_VERSION << std::endl; + #endif + auto first = std::begin(points); auto last = std::end(points); -- cgit v1.2.3 From 77e7ee6e197aa8f0cf0fc0065c8d12e7c543e21f Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 4 Nov 2019 17:41:24 -0500 Subject: fixed typos --- src/python/gudhi/sktda/kernel_methods.py | 2 +- src/python/gudhi/sktda/vector_methods.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index b49bdf60..20cda49b 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -22,7 +22,7 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): Attributes: bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines evenly sampled on [-pi,pi] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + num_directions (int): number of lines evenly sampled on [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. """ self.bandwidth = bandwidth self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index d767a952..1f304eaf 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -376,7 +376,7 @@ class TopologicalVector(BaseEstimator, TransformerMixin): Constructor for the TopologicalVector class. Attributes: - threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If , this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. + threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. """ self.threshold = threshold -- cgit v1.2.3 From 38135d576d92cea7b4e355e2e70a4fe322d4b6e7 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Mon, 4 Nov 2019 18:18:10 -0500 Subject: fixes to Marc's comments --- src/python/example/ex_diagrams.py | 2 +- src/python/gudhi/sktda/kernel_methods.py | 2 +- src/python/gudhi/sktda/metrics.py | 2 +- src/python/gudhi/sktda/vector_methods.py | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py index f12304bd..a6a36b7c 100755 --- a/src/python/example/ex_diagrams.py +++ b/src/python/example/ex_diagrams.py @@ -47,7 +47,7 @@ plt.plot(bc[0]) plt.title("Betti Curve") plt.show() -CP = ComplexPolynomial(threshold=-1, F="T") +CP = ComplexPolynomial(threshold=-1, polynomial_type="T") cp = CP.fit_transform(diags) print("Complex polynomial is " + str(cp[0,:])) diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index 20cda49b..b8d4ab3a 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -22,7 +22,7 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): Attributes: bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines evenly sampled on [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. """ self.bandwidth = bandwidth self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index 816441b6..e85fd14d 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -26,7 +26,7 @@ class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): Constructor for the SlicedWassersteinDistance class. Attributes: - num_directions (int): number of lines to sample uniformly from [-pi,pi] in order to approximate and speed up the distance computation (default 10). + num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the distance computation (default 10). """ self.num_directions = num_directions thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index 1f304eaf..42ded45f 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -424,15 +424,15 @@ class ComplexPolynomial(BaseEstimator, TransformerMixin): """ This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. """ - def __init__(self, F="R", threshold=10): + def __init__(self, polynomial_type="R", threshold=10): """ Constructor for the ComplexPolynomial class. Attributes: - F (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). + polynomial_type (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients, i.e. the number of coefficients corresponding to the largest degree terms of the polynomial. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. """ - self.threshold, self.F = threshold, F + self.threshold, self.polynomial_type = threshold, polynomial_type def fit(self, X, y=None): """ @@ -462,13 +462,13 @@ class ComplexPolynomial(BaseEstimator, TransformerMixin): Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) for d in range(len(X)): D, N = X[d], X[d].shape[0] - if self.F == "R": + if self.polynomial_type == "R": roots = D[:,0] + 1j * D[:,1] - elif self.F == "S": + elif self.polynomial_type == "S": alpha = np.linalg.norm(D, axis=1) alpha = np.where(alpha==0, np.ones(N), alpha) roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) - elif self.F == "T": + elif self.polynomial_type == "T": alpha = np.linalg.norm(D, axis=1) roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) coeff = [0] * (N+1) -- cgit v1.2.3 From c9c023d407cd2f8e0d2202531d8cc64ed40fd3e5 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 09:21:30 +0100 Subject: Do not print #define variable --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 0cb4accb..27a4ba04 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -193,8 +193,7 @@ class Alpha_complex { void init_from_range(const InputPointRange& points) { #if CGAL_VERSION_NR < 1050000000 if (Is_Epick_D::value) - std::cerr << "It is strongly advised to use a CGAL version from 5.0 for performance reasons." - << "Your CGAL version is " << CGAL_VERSION << std::endl; + std::cerr << "It is strongly advised to use a CGAL version from 5.0 for performance reasons." << std::endl; #endif auto first = std::begin(points); -- cgit v1.2.3 From 2f2027474924f3c14bc1384e3b704f27c338e09b Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 09:30:39 +0100 Subject: Add Alpha complex tests with Epeck_d - the new default one --- src/Alpha_complex/test/Alpha_complex_unit_test.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Alpha_complex/test/Alpha_complex_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_unit_test.cpp index 01e4cee3..40b3fe09 100644 --- a/src/Alpha_complex/test/Alpha_complex_unit_test.cpp +++ b/src/Alpha_complex/test/Alpha_complex_unit_test.cpp @@ -15,6 +15,7 @@ #include #include +#include #include // float comparison #include @@ -28,12 +29,16 @@ #include // Use dynamic_dimension_tag for the user to be able to set dimension -typedef CGAL::Epick_d< CGAL::Dynamic_dimension_tag > Kernel_d; +typedef CGAL::Epeck_d< CGAL::Dynamic_dimension_tag > Exact_kernel_d; // Use static dimension_tag for the user not to be able to set dimension -typedef CGAL::Epick_d< CGAL::Dimension_tag<3> > Kernel_s; +typedef CGAL::Epeck_d< CGAL::Dimension_tag<3> > Exact_kernel_s; +// Use dynamic_dimension_tag for the user to be able to set dimension +typedef CGAL::Epick_d< CGAL::Dynamic_dimension_tag > Inexact_kernel_d; +// Use static dimension_tag for the user not to be able to set dimension +typedef CGAL::Epick_d< CGAL::Dimension_tag<3> > Inexact_kernel_s; // The triangulation uses the default instantiation of the TriangulationDataStructure template parameter -typedef boost::mpl::list list_of_kernel_variants; +typedef boost::mpl::list list_of_kernel_variants; BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_OFF_file, TestedKernel, list_of_kernel_variants) { // ---------------------------------------------------------------------------- @@ -86,7 +91,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_OFF_file, TestedKernel, list_of } // Use static dimension_tag for the user not to be able to set dimension -typedef CGAL::Epick_d< CGAL::Dimension_tag<4> > Kernel_4; +typedef CGAL::Epeck_d< CGAL::Dimension_tag<4> > Kernel_4; typedef Kernel_4::Point_d Point_4; typedef std::vector Vector_4_Points; -- cgit v1.2.3 From caa4fc8d87c2842442a3ae78bc5d7ed4124253b0 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 15:41:09 +0100 Subject: Use Epeck_d for examples. Add an Epick_d (fast version) example and test it --- .../example/Alpha_complex_from_off.cpp | 5 +- .../example/Alpha_complex_from_points.cpp | 5 +- src/Alpha_complex/example/CMakeLists.txt | 16 +++++- .../example/Fast_alpha_complex_from_off.cpp | 65 ++++++++++++++++++++++ src/Alpha_complex/include/gudhi/Alpha_complex.h | 15 +++-- 5 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp diff --git a/src/Alpha_complex/example/Alpha_complex_from_off.cpp b/src/Alpha_complex/example/Alpha_complex_from_off.cpp index d411e90a..6b5f1a74 100644 --- a/src/Alpha_complex/example/Alpha_complex_from_off.cpp +++ b/src/Alpha_complex/example/Alpha_complex_from_off.cpp @@ -2,8 +2,6 @@ // to construct a simplex_tree from alpha complex #include -#include - #include #include @@ -23,8 +21,7 @@ int main(int argc, char **argv) { // ---------------------------------------------------------------------------- // Init of an alpha complex from an OFF file // ---------------------------------------------------------------------------- - using Kernel = CGAL::Epick_d< CGAL::Dynamic_dimension_tag >; - Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_name); + Gudhi::alpha_complex::Alpha_complex<> alpha_complex_from_file(off_file_name); std::streambuf* streambufffer; std::ofstream ouput_file_stream; diff --git a/src/Alpha_complex/example/Alpha_complex_from_points.cpp b/src/Alpha_complex/example/Alpha_complex_from_points.cpp index 981aa470..6526ca3a 100644 --- a/src/Alpha_complex/example/Alpha_complex_from_points.cpp +++ b/src/Alpha_complex/example/Alpha_complex_from_points.cpp @@ -2,12 +2,13 @@ // to construct a simplex_tree from alpha complex #include -#include +#include #include #include -using Kernel = CGAL::Epick_d< CGAL::Dimension_tag<2> >; +// Explicit dimension 2 Epeck_d kernel +using Kernel = CGAL::Epeck_d< CGAL::Dimension_tag<2> >; using Point = Kernel::Point_d; using Vector_of_points = std::vector; diff --git a/src/Alpha_complex/example/CMakeLists.txt b/src/Alpha_complex/example/CMakeLists.txt index b069b443..b0337934 100644 --- a/src/Alpha_complex/example/CMakeLists.txt +++ b/src/Alpha_complex/example/CMakeLists.txt @@ -5,9 +5,12 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) target_link_libraries(Alpha_complex_example_from_points ${CGAL_LIBRARY}) add_executable ( Alpha_complex_example_from_off Alpha_complex_from_off.cpp ) target_link_libraries(Alpha_complex_example_from_off ${CGAL_LIBRARY}) + add_executable ( Alpha_complex_example_fast_from_off Fast_alpha_complex_from_off.cpp ) + target_link_libraries(Alpha_complex_example_fast_from_off ${CGAL_LIBRARY}) if (TBB_FOUND) target_link_libraries(Alpha_complex_example_from_points ${TBB_LIBRARIES}) target_link_libraries(Alpha_complex_example_from_off ${TBB_LIBRARIES}) + target_link_libraries(Alpha_complex_example_fast_from_off ${TBB_LIBRARIES}) endif() add_test(NAME Alpha_complex_example_from_points COMMAND $) @@ -16,7 +19,13 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" "60.0" "${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_result_60.txt") add_test(NAME Alpha_complex_example_from_off_32 COMMAND $ "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" "32.0" "${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_result_32.txt") - if (DIFF_PATH) + + add_test(NAME Alpha_complex_example_fast_from_off_60 COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" "60.0" "${CMAKE_CURRENT_BINARY_DIR}/fastalphaoffreader_result_60.txt") + add_test(NAME Alpha_complex_example_fast_from_off_32 COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" "32.0" "${CMAKE_CURRENT_BINARY_DIR}/fastalphaoffreader_result_32.txt") + +if (DIFF_PATH) # Do not forget to copy test results files in current binary dir file(COPY "alphaoffreader_for_doc_32.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) file(COPY "alphaoffreader_for_doc_60.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) @@ -25,6 +34,11 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_result_60.txt ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_for_doc_60.txt) add_test(Alpha_complex_example_from_off_32_diff_files ${DIFF_PATH} ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_result_32.txt ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_for_doc_32.txt) + + add_test(Alpha_complex_example_fast_from_off_60_diff_files ${DIFF_PATH} + ${CMAKE_CURRENT_BINARY_DIR}/fastalphaoffreader_result_60.txt ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_for_doc_60.txt) + add_test(Alpha_complex_example_fast_from_off_32_diff_files ${DIFF_PATH} + ${CMAKE_CURRENT_BINARY_DIR}/fastalphaoffreader_result_32.txt ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_for_doc_32.txt) endif() add_executable ( Alpha_complex_example_weighted_3d_from_points Weighted_alpha_complex_3d_from_points.cpp ) diff --git a/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp b/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp new file mode 100644 index 00000000..d6a39a76 --- /dev/null +++ b/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp @@ -0,0 +1,65 @@ +#include +// to construct a simplex_tree from alpha complex +#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 << " filename.off alpha_square_max_value [ouput_file.txt]\n"; + std::cerr << " i.e.: " << progName << " ../../data/points/alphacomplexdoc.off 60.0\n"; + exit(-1); // ----- >> +} + +int main(int argc, char **argv) { + if ((argc != 3) && (argc != 4)) usage(argc, (argv[0] - 1)); + + std::string off_file_name {argv[1]}; + double alpha_square_max_value {atof(argv[2])}; + + // WARNING : CGAL::Epick_d is fast but not safe (unlike CGAL::Epeck_d) + // (i.e. when the points are on a grid) + using Fast_kernel = CGAL::Epick_d; + // ---------------------------------------------------------------------------- + // Init of an alpha complex from an OFF file + // ---------------------------------------------------------------------------- + Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_name); + + std::streambuf* streambufffer; + std::ofstream ouput_file_stream; + + if (argc == 4) { + ouput_file_stream.open(std::string(argv[3])); + streambufffer = ouput_file_stream.rdbuf(); + } else { + streambufffer = std::cout.rdbuf(); + } + + Gudhi::Simplex_tree<> simplex; + if (alpha_complex_from_file.create_complex(simplex, alpha_square_max_value)) { + std::ostream output_stream(streambufffer); + + // ---------------------------------------------------------------------------- + // Display information about the alpha complex + // ---------------------------------------------------------------------------- + output_stream << "Alpha complex is of dimension " << simplex.dimension() << + " - " << simplex.num_simplices() << " simplices - " << + simplex.num_vertices() << " vertices." << std::endl; + + output_stream << "Iterator on alpha complex simplices in the filtration order, with [filtration value]:" << + std::endl; + for (auto f_simplex : simplex.filtration_simplex_range()) { + output_stream << " ( "; + for (auto vertex : simplex.simplex_vertex_range(f_simplex)) { + output_stream << vertex << " "; + } + output_stream << ") -> " << "[" << simplex.filtration(f_simplex) << "] "; + output_stream << std::endl; + } + } + ouput_file_stream.close(); + return 0; +} diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 27a4ba04..4e0e5159 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -24,7 +24,6 @@ #include // For FAST version #include #include // for CGAL::Identity_property_map -#include #include // for CGAL_VERSION_NR #include // for EIGEN_VERSION_AT_LEAST @@ -249,6 +248,8 @@ class Alpha_complex { * @param[in] complex SimplicialComplexForAlpha to be created. * @param[in] max_alpha_square maximum for alpha square value. Default value is +\f$\infty\f$, and there is very * little point using anything else since it does not save time. + * @param[in] exact Exact filtration values computation. Not exact if `Kernel` is not CGAL::Epeck_d. * * @return true if creation succeeds, false otherwise. * @@ -260,7 +261,8 @@ class Alpha_complex { template bool create_complex(SimplicialComplexForAlpha& complex, - Filtration_value max_alpha_square = std::numeric_limits::infinity()) { + Filtration_value max_alpha_square = std::numeric_limits::infinity(), + bool exact = false) { // From SimplicialComplexForAlpha type required to insert into a simplicial complex (with or without subfaces). typedef typename SimplicialComplexForAlpha::Vertex_handle Vertex_handle; typedef typename SimplicialComplexForAlpha::Simplex_handle Simplex_handle; @@ -336,9 +338,12 @@ class Alpha_complex { if (f_simplex_dim > 0) { // squared_radius function initialization Squared_Radius squared_radius = kernel_.compute_squared_radius_d_object(); - CGAL::NT_converter cv; - - alpha_complex_filtration = cv(squared_radius(pointVector.begin(), pointVector.end())); + + if (exact) { + alpha_complex_filtration = CGAL::to_double(CGAL::exact(squared_radius(pointVector.begin(), pointVector.end()))); + } else { + alpha_complex_filtration = CGAL::to_double(squared_radius(pointVector.begin(), pointVector.end())); + } } complex.assign_filtration(f_simplex, alpha_complex_filtration); #ifdef DEBUG_TRACES -- cgit v1.2.3 From f8bc102a7ec525d61e836892df6e1bfef862635f Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 16:46:14 +0100 Subject: Modify alpha_lcomplex_persistence utility for fast, safe and exact versions. Add some tests --- src/Alpha_complex/utilities/CMakeLists.txt | 30 ++++-- .../utilities/alpha_complex_persistence.cpp | 107 +++++++++++++-------- 2 files changed, 86 insertions(+), 51 deletions(-) diff --git a/src/Alpha_complex/utilities/CMakeLists.txt b/src/Alpha_complex/utilities/CMakeLists.txt index 5295f3cd..57b92942 100644 --- a/src/Alpha_complex/utilities/CMakeLists.txt +++ b/src/Alpha_complex/utilities/CMakeLists.txt @@ -7,9 +7,19 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) if (TBB_FOUND) target_link_libraries(alpha_complex_persistence ${TBB_LIBRARIES}) endif(TBB_FOUND) - add_test(NAME Alpha_complex_utilities_alpha_complex_persistence COMMAND $ - "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45") - + add_test(NAME Alpha_complex_utilities_safe_alpha_complex_persistence COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45" "-o" "safe.pers") + add_test(NAME Alpha_complex_utilities_fast_alpha_complex_persistence COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45" "-o" "fast.pers" "-f") + add_test(NAME Alpha_complex_utilities_exact_alpha_complex_persistence COMMAND $ + "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45" "-o" "exact.pers" "-e") + if (DIFF_PATH) + add_test(Alpha_complex_utilities_diff_exact_alpha_complex ${DIFF_PATH} + "exact.pers" "safe.pers") + add_test(Alpha_complex_utilities_diff_fast_alpha_complex ${DIFF_PATH} + "fast.pers" "safe.pers") + endif() + install(TARGETS alpha_complex_persistence DESTINATION bin) add_executable(alpha_complex_3d_persistence alpha_complex_3d_persistence.cpp) @@ -20,21 +30,21 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) add_test(NAME Alpha_complex_utilities_alpha_complex_3d COMMAND $ "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" - "-p" "2" "-m" "0.45" "-o" "safe.pers") + "-p" "2" "-m" "0.45" "-o" "safe_3d.pers") add_test(NAME Alpha_complex_utilities_exact_alpha_complex_3d COMMAND $ "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" - "-p" "2" "-m" "0.45" "-o" "exact.pers" "-e") + "-p" "2" "-m" "0.45" "-o" "exact_3d.pers" "-e") add_test(NAME Alpha_complex_utilities_safe_alpha_complex_3d COMMAND $ "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" - "-p" "2" "-m" "0.45" "-o" "fast.pers" "-f") + "-p" "2" "-m" "0.45" "-o" "fast_3d.pers" "-f") if (DIFF_PATH) - add_test(Alpha_complex_utilities_diff_alpha_complex_3d ${DIFF_PATH} - "exact.pers" "safe.pers") - add_test(Alpha_complex_utilities_diff_alpha_complex_3d ${DIFF_PATH} - "fast.pers" "safe.pers") + add_test(Alpha_complex_utilities_diff_exact_alpha_complex_3d ${DIFF_PATH} + "exact_3d.pers" "safe_3d.pers") + add_test(Alpha_complex_utilities_diff_fast_alpha_complex_3d ${DIFF_PATH} + "fast_3d.pers" "safe_3d.pers") endif() add_test(NAME Alpha_complex_utilities_periodic_alpha_complex_3d_persistence COMMAND $ diff --git a/src/Alpha_complex/utilities/alpha_complex_persistence.cpp b/src/Alpha_complex/utilities/alpha_complex_persistence.cpp index fab7bd30..486347cc 100644 --- a/src/Alpha_complex/utilities/alpha_complex_persistence.cpp +++ b/src/Alpha_complex/utilities/alpha_complex_persistence.cpp @@ -24,63 +24,84 @@ using Simplex_tree = Gudhi::Simplex_tree<>; using Filtration_value = Simplex_tree::Filtration_value; -void program_options(int argc, char *argv[], std::string &off_file_points, std::string &output_file_diag, - Filtration_value &alpha_square_max_value, int &coeff_field_characteristic, - Filtration_value &min_persistence); +void program_options(int argc, char *argv[], std::string &off_file_points, bool &exact, bool &fast, + std::string &output_file_diag, Filtration_value &alpha_square_max_value, + int &coeff_field_characteristic, Filtration_value &min_persistence); int main(int argc, char **argv) { std::string off_file_points; std::string output_file_diag; + bool exact_version = false; + bool fast_version = false; Filtration_value alpha_square_max_value; int coeff_field_characteristic; Filtration_value min_persistence; - program_options(argc, argv, off_file_points, output_file_diag, alpha_square_max_value, coeff_field_characteristic, - min_persistence); + program_options(argc, argv, off_file_points, exact_version, fast_version, output_file_diag, alpha_square_max_value, + coeff_field_characteristic, min_persistence); - // ---------------------------------------------------------------------------- - // Init of an alpha complex from an OFF file - // ---------------------------------------------------------------------------- - using Kernel = CGAL::Epick_d; - Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_points); + if ((exact_version) && (fast_version)) { + std::cerr << "You cannot set the exact and the fast version." << std::endl; + exit(-1); + } Simplex_tree simplex; - if (alpha_complex_from_file.create_complex(simplex, alpha_square_max_value)) { - // ---------------------------------------------------------------------------- - // Display information about the alpha complex - // ---------------------------------------------------------------------------- - std::cout << "Simplicial complex is of dimension " << simplex.dimension() << " - " << simplex.num_simplices() - << " simplices - " << simplex.num_vertices() << " vertices." << std::endl; - - // Sort the simplices in the order of the filtration - simplex.initialize_filtration(); - - std::cout << "Simplex_tree dim: " << simplex.dimension() << std::endl; - // Compute the persistence diagram of the complex - Gudhi::persistent_cohomology::Persistent_cohomology pcoh( - simplex); - // initializes the coefficient field for homology - pcoh.init_coefficients(coeff_field_characteristic); - - pcoh.compute_persistent_cohomology(min_persistence); - - // Output the diagram in filediag - if (output_file_diag.empty()) { - pcoh.output_diagram(); - } else { - std::cout << "Result in file: " << output_file_diag << std::endl; - std::ofstream out(output_file_diag); - pcoh.output_diagram(out); - out.close(); + if (fast_version) { + // WARNING : CGAL::Epick_d is fast but not safe (unlike CGAL::Epeck_d) + // (i.e. when the points are on a grid) + using Fast_kernel = CGAL::Epick_d; + + // Init of an alpha complex from an OFF file + Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_points); + + if (!alpha_complex_from_file.create_complex(simplex, alpha_square_max_value)) { + std::cerr << "Fast Alpha complex simplicial complex creation failed." << std::endl; + exit(-1); } - } + } else { + using Kernel = CGAL::Epeck_d; + // Init of an alpha complex from an OFF file + Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_points); + + if (!alpha_complex_from_file.create_complex(simplex, alpha_square_max_value, exact_version)) { + std::cerr << "Alpha complex simplicial complex creation failed." << std::endl; + exit(-1); + } + } + // ---------------------------------------------------------------------------- + // Display information about the alpha complex + // ---------------------------------------------------------------------------- + std::cout << "Simplicial complex is of dimension " << simplex.dimension() << " - " << simplex.num_simplices() + << " simplices - " << simplex.num_vertices() << " vertices." << std::endl; + + // Sort the simplices in the order of the filtration + simplex.initialize_filtration(); + + std::cout << "Simplex_tree dim: " << simplex.dimension() << std::endl; + // Compute the persistence diagram of the complex + Gudhi::persistent_cohomology::Persistent_cohomology pcoh( + simplex); + // initializes the coefficient field for homology + pcoh.init_coefficients(coeff_field_characteristic); + + pcoh.compute_persistent_cohomology(min_persistence); + + // Output the diagram in filediag + if (output_file_diag.empty()) { + pcoh.output_diagram(); + } else { + std::cout << "Result in file: " << output_file_diag << std::endl; + std::ofstream out(output_file_diag); + pcoh.output_diagram(out); + out.close(); + } return 0; } -void program_options(int argc, char *argv[], std::string &off_file_points, std::string &output_file_diag, - Filtration_value &alpha_square_max_value, int &coeff_field_characteristic, - Filtration_value &min_persistence) { +void program_options(int argc, char *argv[], std::string &off_file_points, bool &exact, bool &fast, + std::string &output_file_diag, Filtration_value &alpha_square_max_value, + int &coeff_field_characteristic, Filtration_value &min_persistence) { namespace po = boost::program_options; po::options_description hidden("Hidden options"); hidden.add_options()("input-file", po::value(&off_file_points), @@ -88,6 +109,10 @@ void program_options(int argc, char *argv[], std::string &off_file_points, std:: po::options_description visible("Allowed options", 100); visible.add_options()("help,h", "produce help message")( + "exact,e", po::bool_switch(&exact), + "To activate exact version of Alpha complex (default is false, not available if fast is set)")( + "fast,f", po::bool_switch(&fast), + "To activate fast version of Alpha complex (default is false, not available if exact is set)")( "output-file,o", po::value(&output_file_diag)->default_value(std::string()), "Name of file in which the persistence diagram is written. Default print in std::cout")( "max-alpha-square-value,r", po::value(&alpha_square_max_value) -- cgit v1.2.3 From 16d84f8c33fe5bdb3b9b5ac14ed13004332a76de Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 16:47:55 +0100 Subject: Modify alpha_complex_persistence utility documentation accordingly to code change --- src/Alpha_complex/utilities/alphacomplex.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Alpha_complex/utilities/alphacomplex.md b/src/Alpha_complex/utilities/alphacomplex.md index fcd16a3b..527598a9 100644 --- a/src/Alpha_complex/utilities/alphacomplex.md +++ b/src/Alpha_complex/utilities/alphacomplex.md @@ -46,6 +46,8 @@ for the Alpha complex construction. coefficient field Z/pZ for computing homology. * `-m [ --min-persistence ]` (default = 0) Minimal lifetime of homology feature to be recorded. Enter a negative value to see zero length intervals. +* `-e [ --exact ]` for the exact computation version. +* `-f [ --fast ]` for the fast computation version. **Example** -- cgit v1.2.3 From 0f6714990421d967a5e76676920ab190acc61364 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 5 Nov 2019 20:49:14 +0100 Subject: Exact version only for CGAL < 5.X --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 4e0e5159..22e17226 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -338,12 +338,18 @@ class Alpha_complex { if (f_simplex_dim > 0) { // squared_radius function initialization Squared_Radius squared_radius = kernel_.compute_squared_radius_d_object(); - + +#if CGAL_VERSION_NR < 1050000000 + // With CGAL >= 4.11 and < 5.X, CGAL::exact do not work as it is always exact values + // This is why it is slow and 5.X is advised + alpha_complex_filtration = CGAL::to_double(squared_radius(pointVector.begin(), pointVector.end())); +#else // CGAL_VERSION_NR < 1050000000 if (exact) { alpha_complex_filtration = CGAL::to_double(CGAL::exact(squared_radius(pointVector.begin(), pointVector.end()))); } else { alpha_complex_filtration = CGAL::to_double(squared_radius(pointVector.begin(), pointVector.end())); } +#endif //CGAL_VERSION_NR < 1050000000 } complex.assign_filtration(f_simplex, alpha_complex_filtration); #ifdef DEBUG_TRACES -- cgit v1.2.3 From b6174ec368be398e5f4bb647120f5e5905b933e6 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 5 Nov 2019 22:30:13 +0100 Subject: Fix size of underlines for sphinx --- src/python/doc/index.rst | 4 ++-- src/python/doc/wasserstein_distance_user.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index 16d918bc..8f27da0d 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -23,7 +23,7 @@ Alpha complex .. include:: alpha_complex_sum.inc Rips complex -------------- +------------ .. include:: rips_complex_sum.inc @@ -74,7 +74,7 @@ Bottleneck distance .. include:: bottleneck_distance_sum.inc Wasserstein distance -=================== +==================== .. include:: wasserstein_distance_sum.inc diff --git a/src/python/doc/wasserstein_distance_user.rst b/src/python/doc/wasserstein_distance_user.rst index bcb7f19d..9ec64e93 100644 --- a/src/python/doc/wasserstein_distance_user.rst +++ b/src/python/doc/wasserstein_distance_user.rst @@ -3,7 +3,7 @@ .. To get rid of WARNING: document isn't included in any toctree Wasserstein distance user manual -=============================== +================================ Definition ---------- -- cgit v1.2.3 From 94391b1cc232c5f66ae3cdadf865554c57f1308a Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 5 Nov 2019 23:01:31 +0100 Subject: Create GUDHI_PYTHON_MODULES_EXTRA without auto-import Put Wasserstein in it. --- src/python/CMakeLists.txt | 3 ++- src/python/doc/wasserstein_distance_user.rst | 6 ++--- src/python/gudhi/__init__.py.in | 4 +-- src/python/test/test_wasserstein_distance.py | 38 +++++++++++++--------------- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 1b1684e1..dbef7183 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -49,7 +49,8 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'alpha_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ") - set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'wasserstein', ") + # Modules that should not be auto-imported in __init__.py + set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ") add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") add_gudhi_debug_info("Cython version ${CYTHON_VERSION}") diff --git a/src/python/doc/wasserstein_distance_user.rst b/src/python/doc/wasserstein_distance_user.rst index bcb7f19d..fc7bd82c 100644 --- a/src/python/doc/wasserstein_distance_user.rst +++ b/src/python/doc/wasserstein_distance_user.rst @@ -13,7 +13,7 @@ This implementation is based on ideas from "Large Scale Computation of Means and Function -------- -.. autofunction:: gudhi.wasserstein_distance +.. autofunction:: gudhi.wasserstein.wasserstein_distance Basic example @@ -24,13 +24,13 @@ Note that persistence diagrams must be submitted as (n x 2) numpy arrays and mus .. testcode:: - import gudhi + import gudhi.wasserstein import numpy as np diag1 = np.array([[2.7, 3.7],[9.6, 14.],[34.2, 34.974]]) diag2 = np.array([[2.8, 4.45],[9.5, 14.1]]) - message = "Wasserstein distance value = " + '%.2f' % gudhi.wasserstein_distance(diag1, diag2, q=2., p=1.) + message = "Wasserstein distance value = " + '%.2f' % gudhi.wasserstein.wasserstein_distance(diag1, diag2, q=2., p=1.) print(message) The output is: diff --git a/src/python/gudhi/__init__.py.in b/src/python/gudhi/__init__.py.in index 28bab0e1..4039c08f 100644 --- a/src/python/gudhi/__init__.py.in +++ b/src/python/gudhi/__init__.py.in @@ -21,13 +21,13 @@ __debug_info__ = @GUDHI_PYTHON_DEBUG_INFO@ from sys import exc_info from importlib import import_module -__all__ = [@GUDHI_PYTHON_MODULES@] +__all__ = [@GUDHI_PYTHON_MODULES@ @GUDHI_PYTHON_MODULES_EXTRA@] __available_modules = '' __missing_modules = '' # try to import * from gudhi.__module_name -for __module_name in __all__: +for __module_name in [@GUDHI_PYTHON_MODULES@]: try: __module = import_module('gudhi.' + __module_name) try: diff --git a/src/python/test/test_wasserstein_distance.py b/src/python/test/test_wasserstein_distance.py index c1b568e2..a6bf9901 100755 --- a/src/python/test/test_wasserstein_distance.py +++ b/src/python/test/test_wasserstein_distance.py @@ -1,4 +1,4 @@ -import gudhi +from gudhi.wasserstein import wasserstein_distance import numpy as np """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. @@ -23,28 +23,26 @@ def test_basic_wasserstein(): diag4 = np.array([[0, 3], [4, 8]]) emptydiag = np.array([[]]) - assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=2., p=1.) == 0. - assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=1.) == 0. - assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=2.) == 0. - assert gudhi.wasserstein_distance(emptydiag, emptydiag, q=2., p=2.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, q=2., p=1.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=1.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=2.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, q=2., p=2.) == 0. - assert gudhi.wasserstein_distance(diag3, emptydiag, q=np.inf, p=1.) == 2. - assert gudhi.wasserstein_distance(diag3, emptydiag, q=1., p=1.) == 4. + assert wasserstein_distance(diag3, emptydiag, q=np.inf, p=1.) == 2. + assert wasserstein_distance(diag3, emptydiag, q=1., p=1.) == 4. - assert gudhi.wasserstein_distance(diag4, emptydiag, q=1., p=2.) == 5. # thank you Pythagorician triplets - assert gudhi.wasserstein_distance(diag4, emptydiag, q=np.inf, p=2.) == 2.5 - assert gudhi.wasserstein_distance(diag4, emptydiag, q=2., p=2.) == 3.5355339059327378 + assert wasserstein_distance(diag4, emptydiag, q=1., p=2.) == 5. # thank you Pythagorician triplets + assert wasserstein_distance(diag4, emptydiag, q=np.inf, p=2.) == 2.5 + assert wasserstein_distance(diag4, emptydiag, q=2., p=2.) == 3.5355339059327378 - assert gudhi.wasserstein_distance(diag1, diag2, q=2., p=1.) == 1.4453593023967701 - assert gudhi.wasserstein_distance(diag1, diag2, q=2.35, p=1.74) == 0.9772734057168739 + assert wasserstein_distance(diag1, diag2, q=2., p=1.) == 1.4453593023967701 + assert wasserstein_distance(diag1, diag2, q=2.35, p=1.74) == 0.9772734057168739 - assert gudhi.wasserstein_distance(diag1, emptydiag, q=2.35, p=1.7863) == 3.141592214572228 + assert wasserstein_distance(diag1, emptydiag, q=2.35, p=1.7863) == 3.141592214572228 - assert gudhi.wasserstein_distance(diag3, diag4, q=1., p=1.) == 3. - assert gudhi.wasserstein_distance(diag3, diag4, q=np.inf, p=1.) == 3. # no diag matching here - assert gudhi.wasserstein_distance(diag3, diag4, q=np.inf, p=2.) == np.sqrt(5) - assert gudhi.wasserstein_distance(diag3, diag4, q=1., p=2.) == np.sqrt(5) - assert gudhi.wasserstein_distance(diag3, diag4, q=4.5, p=2.) == np.sqrt(5) - - + assert wasserstein_distance(diag3, diag4, q=1., p=1.) == 3. + assert wasserstein_distance(diag3, diag4, q=np.inf, p=1.) == 3. # no diag matching here + assert wasserstein_distance(diag3, diag4, q=np.inf, p=2.) == np.sqrt(5) + assert wasserstein_distance(diag3, diag4, q=1., p=2.) == np.sqrt(5) + assert wasserstein_distance(diag3, diag4, q=4.5, p=2.) == np.sqrt(5) -- cgit v1.2.3 From b9bdb2fe42e377a598abceadd8cc4ba4eea8b862 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 5 Nov 2019 23:54:05 +0100 Subject: Fix documentation indentation --- src/python/gudhi/simplex_tree.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 9f490271..90ddf6dd 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -362,7 +362,7 @@ cdef class SimplexTree: value than its faces by increasing the filtration values. :returns: True if any filtration value was modified, - False if the filtration was already non-decreasing. + False if the filtration was already non-decreasing. :rtype: bool -- cgit v1.2.3 From 8732cd18079a12ab813e863839c48b7c9e504fef Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 6 Nov 2019 07:22:06 +0100 Subject: Add doc warnings --- src/Alpha_complex/doc/Intro_alpha_complex.h | 1 + src/common/doc/main_page.md | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Alpha_complex/doc/Intro_alpha_complex.h b/src/Alpha_complex/doc/Intro_alpha_complex.h index b075d1fc..fd6f4081 100644 --- a/src/Alpha_complex/doc/Intro_alpha_complex.h +++ b/src/Alpha_complex/doc/Intro_alpha_complex.h @@ -51,6 +51,7 @@ namespace alpha_complex { * - For people only interested in the topology of the \ref alpha_complex (for instance persistence), * \ref alpha_complex is equivalent to the \ref cech_complex and much smaller if you do not bound the radii. * \ref cech_complex can still make sense in higher dimension precisely because you can bound the radii. + * - For performances reasons, it is advised to use `Alpha_complex` with \ref cgal ≥ 5.0.0. * * \section pointsexample Example from points * diff --git a/src/common/doc/main_page.md b/src/common/doc/main_page.md index d8cbf97f..e8d11fdf 100644 --- a/src/common/doc/main_page.md +++ b/src/common/doc/main_page.md @@ -45,6 +45,7 @@ values of the codimension 1 cofaces that make it not Gabriel otherwise. All simplices that have a filtration value strictly greater than a given alpha squared value are not inserted into the complex.
+ For performances reasons, it is advised to use \ref cgal ≥ 5.0.0. Author: Vincent Rouvreau
-- cgit v1.2.3 From acd8a5a49083cd588aaa859dc4af104ce9090b22 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 6 Nov 2019 13:39:07 +0100 Subject: Update comment for GUDHI_PYTHON_MODULES_EXTRA --- src/python/gudhi/__init__.py.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/python/gudhi/__init__.py.in b/src/python/gudhi/__init__.py.in index 4039c08f..02888fff 100644 --- a/src/python/gudhi/__init__.py.in +++ b/src/python/gudhi/__init__.py.in @@ -26,7 +26,10 @@ __all__ = [@GUDHI_PYTHON_MODULES@ @GUDHI_PYTHON_MODULES_EXTRA@] __available_modules = '' __missing_modules = '' -# try to import * from gudhi.__module_name +# Try to import * from gudhi.__module_name for default modules. +# Extra modules require an explicit import by the user (mostly because of +# unusual dependencies, but also to avoid cluttering namespace gudhi and +# speed up the basic import) for __module_name in [@GUDHI_PYTHON_MODULES@]: try: __module = import_module('gudhi.' + __module_name) -- cgit v1.2.3 From 64cd8ab00b5868626137d4f7838741cdeb2c5d08 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 6 Nov 2019 16:03:00 +0100 Subject: Also document __init__ --- src/python/doc/sktda.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python/doc/sktda.rst b/src/python/doc/sktda.rst index d36d9ab4..9acec616 100644 --- a/src/python/doc/sktda.rst +++ b/src/python/doc/sktda.rst @@ -10,26 +10,26 @@ Preprocessing ------------- .. automodule:: gudhi.sktda.preprocessing :members: - :undoc-members: + :special-members: :show-inheritance: Vector methods -------------- .. automodule:: gudhi.sktda.vector_methods :members: - :undoc-members: + :special-members: :show-inheritance: Kernel methods -------------- .. automodule:: gudhi.sktda.kernel_methods :members: - :undoc-members: + :special-members: :show-inheritance: Metrics ------- .. automodule:: gudhi.sktda.metrics :members: - :undoc-members: + :special-members: :show-inheritance: -- cgit v1.2.3 From 4ee6ed3caa55eb8e2aec9e4bd69bb8b4561dde7e Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 7 Nov 2019 16:31:16 +0100 Subject: Doc review : This remark is only available for the dD case, not for the 3d case. --- src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index cb9cb926..70e864e6 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -97,7 +97,7 @@ struct Value_from_iterator { * \details * The data structure is constructing a CGAL 3D Alpha * Shapes from a range of points (can be read from an OFF file, cf. Points_off_reader). - * Duplicate points are inserted once in the Alpha_complex. This is the reason why the vertices may be not contiguous. + * Duplicate points are inserted once in the Alpha_complex. * * \tparam Complexity shall be `Gudhi::alpha_complex::complexity` type. Default value is * `Gudhi::alpha_complex::complexity::SAFE`. -- cgit v1.2.3 From 98469d9e80a881ab4dae6358b7a7e64ce90784b2 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 7 Nov 2019 16:39:36 +0100 Subject: Code review: replace streambufffer with streambuffer - was copied/ pasted from several places --- src/Alpha_complex/example/Alpha_complex_from_off.cpp | 8 ++++---- src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp | 8 ++++---- .../example_rips_complex_from_csv_distance_matrix_file.cpp | 8 ++++---- src/Rips_complex/example/example_rips_complex_from_off_file.cpp | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Alpha_complex/example/Alpha_complex_from_off.cpp b/src/Alpha_complex/example/Alpha_complex_from_off.cpp index 6b5f1a74..220a66de 100644 --- a/src/Alpha_complex/example/Alpha_complex_from_off.cpp +++ b/src/Alpha_complex/example/Alpha_complex_from_off.cpp @@ -23,19 +23,19 @@ int main(int argc, char **argv) { // ---------------------------------------------------------------------------- Gudhi::alpha_complex::Alpha_complex<> alpha_complex_from_file(off_file_name); - std::streambuf* streambufffer; + std::streambuf* streambuffer; std::ofstream ouput_file_stream; if (argc == 4) { ouput_file_stream.open(std::string(argv[3])); - streambufffer = ouput_file_stream.rdbuf(); + streambuffer = ouput_file_stream.rdbuf(); } else { - streambufffer = std::cout.rdbuf(); + streambuffer = std::cout.rdbuf(); } Gudhi::Simplex_tree<> simplex; if (alpha_complex_from_file.create_complex(simplex, alpha_square_max_value)) { - std::ostream output_stream(streambufffer); + std::ostream output_stream(streambuffer); // ---------------------------------------------------------------------------- // Display information about the alpha complex diff --git a/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp b/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp index d6a39a76..f181005a 100644 --- a/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp +++ b/src/Alpha_complex/example/Fast_alpha_complex_from_off.cpp @@ -28,19 +28,19 @@ int main(int argc, char **argv) { // ---------------------------------------------------------------------------- Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_name); - std::streambuf* streambufffer; + std::streambuf* streambuffer; std::ofstream ouput_file_stream; if (argc == 4) { ouput_file_stream.open(std::string(argv[3])); - streambufffer = ouput_file_stream.rdbuf(); + streambuffer = ouput_file_stream.rdbuf(); } else { - streambufffer = std::cout.rdbuf(); + streambuffer = std::cout.rdbuf(); } Gudhi::Simplex_tree<> simplex; if (alpha_complex_from_file.create_complex(simplex, alpha_square_max_value)) { - std::ostream output_stream(streambufffer); + std::ostream output_stream(streambuffer); // ---------------------------------------------------------------------------- // Display information about the alpha complex diff --git a/src/Rips_complex/example/example_rips_complex_from_csv_distance_matrix_file.cpp b/src/Rips_complex/example/example_rips_complex_from_csv_distance_matrix_file.cpp index 9e182f1e..b7040453 100644 --- a/src/Rips_complex/example/example_rips_complex_from_csv_distance_matrix_file.cpp +++ b/src/Rips_complex/example/example_rips_complex_from_csv_distance_matrix_file.cpp @@ -35,19 +35,19 @@ int main(int argc, char **argv) { Distance_matrix distances = Gudhi::read_lower_triangular_matrix_from_csv_file(csv_file_name); Rips_complex rips_complex_from_file(distances, threshold); - std::streambuf* streambufffer; + std::streambuf* streambuffer; std::ofstream ouput_file_stream; if (argc == 5) { ouput_file_stream.open(std::string(argv[4])); - streambufffer = ouput_file_stream.rdbuf(); + streambuffer = ouput_file_stream.rdbuf(); } else { - streambufffer = std::cout.rdbuf(); + streambuffer = std::cout.rdbuf(); } Simplex_tree stree; rips_complex_from_file.create_complex(stree, dim_max); - std::ostream output_stream(streambufffer); + std::ostream output_stream(streambuffer); // ---------------------------------------------------------------------------- // Display information about the Rips complex diff --git a/src/Rips_complex/example/example_rips_complex_from_off_file.cpp b/src/Rips_complex/example/example_rips_complex_from_off_file.cpp index de2e4ea4..36b468a7 100644 --- a/src/Rips_complex/example/example_rips_complex_from_off_file.cpp +++ b/src/Rips_complex/example/example_rips_complex_from_off_file.cpp @@ -34,19 +34,19 @@ int main(int argc, char **argv) { Gudhi::Points_off_reader off_reader(off_file_name); Rips_complex rips_complex_from_file(off_reader.get_point_cloud(), threshold, Gudhi::Euclidean_distance()); - std::streambuf* streambufffer; + std::streambuf* streambuffer; std::ofstream ouput_file_stream; if (argc == 5) { ouput_file_stream.open(std::string(argv[4])); - streambufffer = ouput_file_stream.rdbuf(); + streambuffer = ouput_file_stream.rdbuf(); } else { - streambufffer = std::cout.rdbuf(); + streambuffer = std::cout.rdbuf(); } Simplex_tree stree; rips_complex_from_file.create_complex(stree, dim_max); - std::ostream output_stream(streambufffer); + std::ostream output_stream(streambuffer); // ---------------------------------------------------------------------------- // Display information about the Rips complex -- cgit v1.2.3 From ea296b4a93c37b0e19d4605c1be3e950ed8cd3c2 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 7 Nov 2019 16:45:28 +0100 Subject: Code review: it is with Epeck_d that it is advised to have CGAL >= 5.0. Error message clarification --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 22e17226..9aa30383 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -50,8 +50,8 @@ namespace Gudhi { namespace alpha_complex { -template struct Is_Epick_D { static const bool value = false; }; -template struct Is_Epick_D> { static const bool value = true; }; +template struct Is_Epeck_D { static const bool value = false; }; +template struct Is_Epeck_D> { static const bool value = true; }; /** * \class Alpha_complex Alpha_complex.h gudhi/Alpha_complex.h @@ -191,8 +191,9 @@ class Alpha_complex { template void init_from_range(const InputPointRange& points) { #if CGAL_VERSION_NR < 1050000000 - if (Is_Epick_D::value) - std::cerr << "It is strongly advised to use a CGAL version from 5.0 for performance reasons." << std::endl; + if (Is_Epeck_D::value) + std::cerr << "It is strongly advised to use a CGAL version >= 5.0 with Epeck_d Kernel for performance reasons." + << std::endl; #endif auto first = std::begin(points); -- cgit v1.2.3 From 5a2c3559da581136edb7facf5578bd1443080ef3 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 7 Nov 2019 17:02:38 +0100 Subject: Code review: Add suggested code using NT_converter to simplify the different alpha_complex_filtration computations cases --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 9aa30383..b3d1e6ea 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -25,6 +25,7 @@ #include #include // for CGAL::Identity_property_map #include // for CGAL_VERSION_NR +#include #include // for EIGEN_VERSION_AT_LEAST @@ -340,17 +341,12 @@ class Alpha_complex { // squared_radius function initialization Squared_Radius squared_radius = kernel_.compute_squared_radius_d_object(); -#if CGAL_VERSION_NR < 1050000000 - // With CGAL >= 4.11 and < 5.X, CGAL::exact do not work as it is always exact values - // This is why it is slow and 5.X is advised - alpha_complex_filtration = CGAL::to_double(squared_radius(pointVector.begin(), pointVector.end())); -#else // CGAL_VERSION_NR < 1050000000 - if (exact) { - alpha_complex_filtration = CGAL::to_double(CGAL::exact(squared_radius(pointVector.begin(), pointVector.end()))); - } else { - alpha_complex_filtration = CGAL::to_double(squared_radius(pointVector.begin(), pointVector.end())); - } -#endif //CGAL_VERSION_NR < 1050000000 + CGAL::NT_converter cv; + auto sqrad = squared_radius(pointVector.begin(), pointVector.end()); +#if CGAL_VERSION_NR >= 1050000000 + if(exact) CGAL::exact(sqrad); +#endif + alpha_complex_filtration = cv(sqrad); } complex.assign_filtration(f_simplex, alpha_complex_filtration); #ifdef DEBUG_TRACES -- cgit v1.2.3 From 7c80dd28eb16e70316e6acc0bde8f698f79b2003 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 8 Nov 2019 21:01:20 +0100 Subject: Enable the napoleon extension for sphinx. --- src/python/doc/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/python/doc/conf.py b/src/python/doc/conf.py index e4c718c3..64d9cba1 100755 --- a/src/python/doc/conf.py +++ b/src/python/doc/conf.py @@ -39,6 +39,7 @@ extensions = [ 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', 'sphinxcontrib.bibtex', ] -- cgit v1.2.3 From dc9771161ec5155030dad9301cd9af79f13005b3 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 8 Nov 2019 21:19:58 +0100 Subject: Move sktda to GUDHI_PYTHON_MODULES_EXTRA. --- src/python/CMakeLists.txt | 66 ++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 2cc578a6..5e2207f0 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -49,8 +49,8 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'alpha_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ") - set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'sktda', ") # Modules that should not be auto-imported in __init__.py + set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'sktda', ") set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ") add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") @@ -69,6 +69,7 @@ if(PYTHONINTERP_FOUND) endif() if(SKLEARN_FOUND) add_gudhi_debug_info("Scikit-learn version ${SKLEARN_VERSION}") + endif() if(OT_FOUND) add_gudhi_debug_info("POT version ${OT_VERSION}") endif() @@ -391,37 +392,42 @@ if(PYTHONINTERP_FOUND) if(MATPLOTLIB_FOUND) if(NUMPY_FOUND) if(SCIPY_FOUND) - if(OT_FOUND) - if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/") - # User warning - Sphinx is a static pages generator, and configured to work fine with user_version - # Images and biblio warnings because not found on developper version - if (GUDHI_PYTHON_PATH STREQUAL "src/python") - set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developper version. Images and biblio will miss") - endif() - # sphinx target requires gudhi.so, because conf.py reads gudhi version from it - add_custom_target(sphinx - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gudhi.so" - COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM) - - add_test(NAME sphinx_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest) - - # Set missing or not modules - set(GUDHI_MODULES ${GUDHI_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MODULES") - else(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - message("++ Python documentation module will not be compiled because it requires a Eigen3 and CGAL version >= 4.11.0") + if(SKLEARN_FOUND) + if(OT_FOUND) + if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/") + # User warning - Sphinx is a static pages generator, and configured to work fine with user_version + # Images and biblio warnings because not found on developper version + if (GUDHI_PYTHON_PATH STREQUAL "src/python") + set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developper version. Images and biblio will miss") + endif() + # sphinx target requires gudhi.so, because conf.py reads gudhi version from it + add_custom_target(sphinx + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gudhi.so" + COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM) + + add_test(NAME sphinx_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest) + + # Set missing or not modules + set(GUDHI_MODULES ${GUDHI_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MODULES") + else(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + message("++ Python documentation module will not be compiled because it requires a Eigen3 and CGAL version >= 4.11.0") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") + endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + else(OT_FOUND) + message("++ Python documentation module will not be compiled because POT was not found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") - endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - else(OT_FOUND) - message("++ Python documentation module will not be compiled because POT was not found") + endif(OT_FOUND) + else(SKLEARN_FOUND) + message("++ Python documentation module will not be compiled because scikit-learn was not found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") - endif(OT_FOUND) + endif(SKLEARN_FOUND) else(SCIPY_FOUND) message("++ Python documentation module will not be compiled because scipy was not found") set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") -- cgit v1.2.3 From d091cf5369c2eba0bb301942842d56606a45af76 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 9 Nov 2019 21:03:13 +0100 Subject: Replace 'is' with == or similar. Related to issue #113, but I did not touch persistence_graphical_tools.py which is handled in PR #119. I don't know if I got them all, I guess we'll start seeing the warnings with python-3.8 soon enough. --- src/python/gudhi/euclidean_strong_witness_complex.pyx | 2 +- src/python/gudhi/euclidean_witness_complex.pyx | 2 +- src/python/gudhi/off_reader.pyx | 2 +- src/python/gudhi/periodic_cubical_complex.pyx | 4 ++-- src/python/gudhi/reader_utils.pyx | 6 +++--- src/python/gudhi/strong_witness_complex.pyx | 2 +- src/python/gudhi/subsampling.pyx | 14 +++++++------- src/python/gudhi/tangential_complex.pyx | 2 +- src/python/gudhi/witness_complex.pyx | 2 +- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/python/gudhi/euclidean_strong_witness_complex.pyx b/src/python/gudhi/euclidean_strong_witness_complex.pyx index 5d6e4fb9..0e5b544a 100644 --- a/src/python/gudhi/euclidean_strong_witness_complex.pyx +++ b/src/python/gudhi/euclidean_strong_witness_complex.pyx @@ -71,7 +71,7 @@ cdef class EuclideanStrongWitnessComplex: """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - if limit_dimension is not -1: + if limit_dimension != -1: self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: diff --git a/src/python/gudhi/euclidean_witness_complex.pyx b/src/python/gudhi/euclidean_witness_complex.pyx index 2531919b..86d2d996 100644 --- a/src/python/gudhi/euclidean_witness_complex.pyx +++ b/src/python/gudhi/euclidean_witness_complex.pyx @@ -71,7 +71,7 @@ cdef class EuclideanWitnessComplex: """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - if limit_dimension is not -1: + if limit_dimension != -1: self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: diff --git a/src/python/gudhi/off_reader.pyx b/src/python/gudhi/off_reader.pyx index 9efd97ff..27a89870 100644 --- a/src/python/gudhi/off_reader.pyx +++ b/src/python/gudhi/off_reader.pyx @@ -29,7 +29,7 @@ def read_off(off_file=''): :returns: The point set. :rtype: vector[vector[double]] """ - if off_file is not '': + if off_file: if os.path.isfile(off_file): return read_points_from_OFF_file(str.encode(off_file)) else: diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index 724fadd4..88223afb 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -68,9 +68,9 @@ cdef class PeriodicCubicalComplex: # The real cython constructor def __cinit__(self, dimensions=None, top_dimensional_cells=None, periodic_dimensions=None, perseus_file=''): - if (dimensions is not None) and (top_dimensional_cells is not None) and (periodic_dimensions is not None) and (perseus_file is ''): + if (dimensions is not None) and (top_dimensional_cells is not None) and (periodic_dimensions is not None) and (perseus_file == ''): self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, top_dimensional_cells, periodic_dimensions) - elif (dimensions is None) and (top_dimensional_cells is None) and (periodic_dimensions is None) and (perseus_file is not ''): + elif (dimensions is None) and (top_dimensional_cells is None) and (periodic_dimensions is None) and (perseus_file != ''): if os.path.isfile(perseus_file): self.thisptr = new Periodic_cubical_complex_base_interface(str.encode(perseus_file)) else: diff --git a/src/python/gudhi/reader_utils.pyx b/src/python/gudhi/reader_utils.pyx index 147fae71..b266c783 100644 --- a/src/python/gudhi/reader_utils.pyx +++ b/src/python/gudhi/reader_utils.pyx @@ -37,7 +37,7 @@ def read_lower_triangular_matrix_from_csv_file(csv_file='', separator=';'): :returns: The lower triangular matrix. :rtype: vector[vector[double]] """ - if csv_file is not '': + if csv_file: if path.isfile(csv_file): return read_matrix_from_csv_file(str.encode(csv_file), ord(separator[0])) print("file " + csv_file + " not set or not found.") @@ -56,7 +56,7 @@ def read_persistence_intervals_grouped_by_dimension(persistence_file=''): :returns: The persistence pairs grouped by dimension. :rtype: map[int, vector[pair[double, double]]] """ - if persistence_file is not '': + if persistence_file: if path.isfile(persistence_file): return read_pers_intervals_grouped_by_dimension(str.encode(persistence_file)) print("file " + persistence_file + " not set or not found.") @@ -79,7 +79,7 @@ def read_persistence_intervals_in_dimension(persistence_file='', only_this_dim=- :returns: The persistence intervals. :rtype: numpy array of dimension 2 """ - if persistence_file is not '': + if persistence_file: if path.isfile(persistence_file): return np_array(read_pers_intervals_in_dimension(str.encode( persistence_file), only_this_dim)) diff --git a/src/python/gudhi/strong_witness_complex.pyx b/src/python/gudhi/strong_witness_complex.pyx index e757abea..e35ba6ad 100644 --- a/src/python/gudhi/strong_witness_complex.pyx +++ b/src/python/gudhi/strong_witness_complex.pyx @@ -69,7 +69,7 @@ cdef class StrongWitnessComplex: """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - if limit_dimension is not -1: + if limit_dimension != -1: self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: diff --git a/src/python/gudhi/subsampling.pyx b/src/python/gudhi/subsampling.pyx index 1135c1fb..e622ac99 100644 --- a/src/python/gudhi/subsampling.pyx +++ b/src/python/gudhi/subsampling.pyx @@ -44,15 +44,15 @@ def choose_n_farthest_points(points=None, off_file='', nb_points=0, starting_poi :param nb_points: Number of points of the subsample. :type nb_points: unsigned. :param starting_point: The iteration starts with the landmark `starting \ - point`,which is the index of the poit to start with. If not set, this \ - index is choosen randomly. + point`,which is the index of the point to start with. If not set, this \ + index is chosen randomly. :type starting_point: unsigned. :returns: The subsample point set. :rtype: vector[vector[double]] """ - if off_file is not '': + if off_file: if os.path.isfile(off_file): - if starting_point is '': + if starting_point == '': return subsampling_n_farthest_points_from_file(str.encode(off_file), nb_points) else: @@ -65,7 +65,7 @@ def choose_n_farthest_points(points=None, off_file='', nb_points=0, starting_poi if points is None: # Empty points points=[] - if starting_point is '': + if starting_point == '': return subsampling_n_farthest_points(points, nb_points) else: return subsampling_n_farthest_points(points, nb_points, @@ -87,7 +87,7 @@ def pick_n_random_points(points=None, off_file='', nb_points=0): :returns: The subsample point set. :rtype: vector[vector[double]] """ - if off_file is not '': + if off_file: if os.path.isfile(off_file): return subsampling_n_random_points_from_file(str.encode(off_file), nb_points) @@ -117,7 +117,7 @@ def sparsify_point_set(points=None, off_file='', min_squared_dist=0.0): :returns: The subsample point set. :rtype: vector[vector[double]] """ - if off_file is not '': + if off_file: if os.path.isfile(off_file): return subsampling_sparsify_points_from_file(str.encode(off_file), min_squared_dist) diff --git a/src/python/gudhi/tangential_complex.pyx b/src/python/gudhi/tangential_complex.pyx index 3a945fe2..9ef15567 100644 --- a/src/python/gudhi/tangential_complex.pyx +++ b/src/python/gudhi/tangential_complex.pyx @@ -65,7 +65,7 @@ cdef class TangentialComplex: # The real cython constructor def __cinit__(self, intrisic_dim, points=None, off_file=''): - if off_file is not '': + if off_file: if os.path.isfile(off_file): self.thisptr = new Tangential_complex_interface(intrisic_dim, str.encode(off_file), True) else: diff --git a/src/python/gudhi/witness_complex.pyx b/src/python/gudhi/witness_complex.pyx index baa70b7a..c19ccf2b 100644 --- a/src/python/gudhi/witness_complex.pyx +++ b/src/python/gudhi/witness_complex.pyx @@ -69,7 +69,7 @@ cdef class WitnessComplex: """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - if limit_dimension is not -1: + if limit_dimension != -1: self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: -- cgit v1.2.3 From 1e5d0ca3f84f1e5c132014d3cfc96bfe0aa3d7ee Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 9 Nov 2019 21:41:06 +0100 Subject: Empty complexes have 0 (not -1) Betti numbers --- src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h | 8 ++++++-- src/Persistent_cohomology/test/betti_numbers_unit_test.cpp | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h index 944b6d35..0f1876d0 100644 --- a/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h +++ b/src/Persistent_cohomology/include/gudhi/Persistent_cohomology.h @@ -600,8 +600,10 @@ class Persistent_cohomology { * @return A vector of Betti numbers. */ std::vector betti_numbers() const { + // Don't allocate a vector of negative size for an empty complex + int siz = std::max(dim_max_, 0); // Init Betti numbers vector with zeros until Simplicial complex dimension - std::vector betti_numbers(dim_max_, 0); + std::vector betti_numbers(siz); for (auto pair : persistent_pairs_) { // Count never ended persistence intervals @@ -639,8 +641,10 @@ class Persistent_cohomology { * @return A vector of persistent Betti numbers. */ std::vector persistent_betti_numbers(Filtration_value from, Filtration_value to) const { + // Don't allocate a vector of negative size for an empty complex + int siz = std::max(dim_max_, 0); // Init Betti numbers vector with zeros until Simplicial complex dimension - std::vector betti_numbers(dim_max_, 0); + std::vector betti_numbers(siz); for (auto pair : persistent_pairs_) { // Count persistence intervals that covers the given interval // null_simplex test : if the function is called with to=+infinity, we still get something useful. And it will diff --git a/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp b/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp index 0a08d200..b9f11607 100644 --- a/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp +++ b/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp @@ -284,4 +284,13 @@ BOOST_AUTO_TEST_CASE( betti_numbers ) auto intervals_in_dimension_2 = pcoh.intervals_in_dimension(2); std::cout << "intervals_in_dimension_2.size() = " << intervals_in_dimension_2.size() << std::endl; BOOST_CHECK(intervals_in_dimension_2.size() == 0); + + std::cout << "EMPTY COMPLEX" << std::endl; + Simplex_tree empty; + empty.initialize_filtration(); + St_persistence pcoh_empty(empty, false); + pcoh_empty.init_coefficients(2); + pcoh_empty.compute_persistent_cohomology(); + BOOST_CHECK(pcoh_empty.betti_numbers().size() == 0); + BOOST_CHECK(pcoh_empty.persistent_betti_numbers(0,1).size() == 0); } -- cgit v1.2.3 From 5372648ccc80e9754e1d2057c5bdba9724810da5 Mon Sep 17 00:00:00 2001 From: mathieu Date: Sat, 9 Nov 2019 21:01:44 -0500 Subject: added copyrights --- src/python/gudhi/sktda/kernel_methods.py | 13 +++++++++---- src/python/gudhi/sktda/metrics.py | 11 ++++++++--- src/python/gudhi/sktda/preprocessing.py | 11 ++++++++--- src/python/gudhi/sktda/vector_methods.py | 11 ++++++++--- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index b8d4ab3a..0a0d333d 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -1,6 +1,11 @@ -""" -@author: Mathieu Carriere -All rights reserved +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Mathieu Carrière + + Copyright (C) 2018-2019 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification """ import numpy as np @@ -140,7 +145,7 @@ class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): """ self.diagrams_ = list(X) for i in range(len(self.diagrams_)): - op_D = np.matmul(self.diagrams_[i], np.array([[0.,1.], [1.,0.]])) + op_D = self.diagrams_[i][:,[1,0]] self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) self.pwg_.fit(X) return self diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index e85fd14d..cc461cbd 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -1,6 +1,11 @@ -""" -@author: Mathieu Carriere -All rights reserved +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Mathieu Carrière + + Copyright (C) 2018-2019 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification """ import numpy as np diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 784e300f..8c0ca2c4 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -1,6 +1,11 @@ -""" -@author: Mathieu Carriere -All rights reserved +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Mathieu Carrière + + Copyright (C) 2018-2019 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification """ import numpy as np diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index 42ded45f..2b72b173 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -1,6 +1,11 @@ -""" -@author: Mathieu Carriere -All rights reserved +""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + Author(s): Mathieu Carrière + + Copyright (C) 2018-2019 Inria + + Modification(s): + - YYYY/MM Author: Description of the modification """ import numpy as np -- cgit v1.2.3 From 184587947b18daf57583c7cb111ad315b8d48a43 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau <10407034+VincentRouvreau@users.noreply.github.com> Date: Mon, 11 Nov 2019 22:34:38 +0100 Subject: Add scikit-learn to CI for sklearn-tda branch --- Dockerfile_for_circleci_image | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile_for_circleci_image b/Dockerfile_for_circleci_image index 12f2dc94..0dd408fc 100644 --- a/Dockerfile_for_circleci_image +++ b/Dockerfile_for_circleci_image @@ -52,6 +52,7 @@ RUN pip3 install \ scipy \ Cython \ POT \ + scikit-learn \ sphinx \ sphinxcontrib-bibtex -- cgit v1.2.3 From 0dd2d14094075f8b4a10b9f569996e2ad271cd6c Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 12:00:17 +0100 Subject: Add link from the main doc page. --- src/python/doc/index.rst | 5 +++++ src/python/doc/sktda_sum.inc | 14 ++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/python/doc/sktda_sum.inc diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index 8f27da0d..f8dd3c2f 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -78,6 +78,11 @@ Wasserstein distance .. include:: wasserstein_distance_sum.inc +Persistence representations +=========================== + +.. include:: sktda_sum.inc + Persistence graphical tools =========================== diff --git a/src/python/doc/sktda_sum.inc b/src/python/doc/sktda_sum.inc new file mode 100644 index 00000000..a0f77e7e --- /dev/null +++ b/src/python/doc/sktda_sum.inc @@ -0,0 +1,14 @@ +.. table:: + :widths: 30 50 20 + + +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ + | .. figure:: | Vectorizations, distances and kernels that work on persistence | :Author: Mathieu Carrière | + | ../../doc/Persistence_representations/average_landscape.png | diagrams, compatible with scikit-learn. | | + | | | :Introduced in: GUDHI 3.1.0 | + | | | | + | | | :Copyright: MIT | + | | | | + | | | :Requires: scikit-learn | + +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ + | * :doc:`sktda` | + +------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+ -- cgit v1.2.3 From 0fa77a12509ea6d51043569b592bad855f276a45 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 13:26:33 +0100 Subject: Make the license a comment instead of a docstring We don't want it in the autogenerated sphinx doc --- src/python/gudhi/sktda/kernel_methods.py | 17 ++++++++--------- src/python/gudhi/sktda/metrics.py | 17 ++++++++--------- src/python/gudhi/sktda/preprocessing.py | 17 ++++++++--------- src/python/gudhi/sktda/vector_methods.py | 17 ++++++++--------- 4 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index 0a0d333d..24067718 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -1,12 +1,11 @@ -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Mathieu Carrière - - Copyright (C) 2018-2019 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification import numpy as np from sklearn.base import BaseEstimator, TransformerMixin diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index cc461cbd..b8acf261 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -1,12 +1,11 @@ -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Mathieu Carrière - - Copyright (C) 2018-2019 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification import numpy as np from sklearn.base import BaseEstimator, TransformerMixin diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 8c0ca2c4..6bc7ec52 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -1,12 +1,11 @@ -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Mathieu Carrière - - Copyright (C) 2018-2019 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification import numpy as np from sklearn.base import BaseEstimator, TransformerMixin diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index 2b72b173..c1824ebb 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -1,12 +1,11 @@ -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Mathieu Carrière - - Copyright (C) 2018-2019 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification import numpy as np from sklearn.base import BaseEstimator, TransformerMixin -- cgit v1.2.3 From 8427713bc748bc040dd696a64d81b3fe6f648a07 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 13:37:08 +0100 Subject: Remove mentions of the exact sliced Wasserstein (not implemented) --- src/python/gudhi/sktda/kernel_methods.py | 2 +- src/python/gudhi/sktda/metrics.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index 24067718..d409accd 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -26,7 +26,7 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): Attributes: bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). If -1, the exact kernel is computed. + num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). """ self.bandwidth = bandwidth self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index b8acf261..f55f553b 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -15,7 +15,7 @@ try: USE_GUDHI = True except ImportError: USE_GUDHI = False - print("Gudhi not found: BottleneckDistance will return null matrix, and exact SlicedWassersteinDistance not available") + print("Gudhi built without CGAL: BottleneckDistance will return a null matrix") ############################################# # Metrics ################################### -- cgit v1.2.3 From 3b58332d4f5849dd05ee08d8a222ca0fe9475832 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 13:54:35 +0100 Subject: Syntax of return type in docstring --- src/python/gudhi/sktda/kernel_methods.py | 8 ++++---- src/python/gudhi/sktda/metrics.py | 6 +++--- src/python/gudhi/sktda/preprocessing.py | 12 ++++++------ src/python/gudhi/sktda/vector_methods.py | 14 +++++++------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index d409accd..e93138e6 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -50,7 +50,7 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein kernel values. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein kernel values. """ return np.exp(-self.sw_.transform(X)/self.bandwidth) @@ -92,7 +92,7 @@ class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence weighted Gaussian kernel values. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence weighted Gaussian kernel values. """ Xp = list(X) Xfit = np.zeros((len(Xp), len(self.diagrams_))) @@ -157,7 +157,7 @@ class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence scale space kernel values. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence scale space kernel values. """ Xp = list(X) for i in range(len(Xp)): @@ -200,7 +200,7 @@ class PersistenceFisherKernel(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher kernel values. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher kernel values. """ return np.exp(-self.pf_.transform(X)/self.bandwidth) diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index f55f553b..c51b8f3b 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -58,7 +58,7 @@ class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise sliced Wasserstein distances. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein distances. """ Xfit = np.zeros((len(X), len(self.approx_))) if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): @@ -114,7 +114,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise bottleneck distances. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise bottleneck distances. """ num_diag1 = len(X) @@ -182,7 +182,7 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X)): matrix of pairwise persistence Fisher distances. + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher distances. """ Xfit = np.zeros((len(X), len(self.diagrams_))) if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 6bc7ec52..294b5aeb 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -43,7 +43,7 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): X (list of n x 2 numpy array): input persistence diagrams. Returns: - Xfit (list of n x 2 numpy array): transformed persistence diagrams. + list of n x 2 numpy array: transformed persistence diagrams. """ Xfit = [] for diag in X: @@ -85,7 +85,7 @@ class Clamping(BaseEstimator, TransformerMixin): X (numpy array of size n): input list of values. Returns: - Xfit (numpy array of size n): output list of values. + numpy array of size n: output list of values. """ Xfit = np.minimum(X, self.limit) #Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) @@ -131,7 +131,7 @@ class DiagramScaler(BaseEstimator, TransformerMixin): X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): transformed persistence diagrams. + list of n x 2 or n x 1 numpy arrays: transformed persistence diagrams. """ Xfit = [np.copy(d) for d in X] if self.use: @@ -174,7 +174,7 @@ class Padding(BaseEstimator, TransformerMixin): X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. Returns: - Xfit (list of n x 3 or n x 2 numpy arrays): padded persistence diagrams. + list of n x 3 or n x 2 numpy arrays: padded persistence diagrams. """ if self.use: Xfit, num_diag = [], len(X) @@ -223,7 +223,7 @@ class ProminentPoints(BaseEstimator, TransformerMixin): X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): thresholded persistence diagrams. + list of n x 2 or n x 1 numpy arrays: thresholded persistence diagrams. """ if self.use: Xfit, num_diag = [], len(X) @@ -292,7 +292,7 @@ class DiagramSelector(BaseEstimator, TransformerMixin): X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. Returns: - Xfit (list of n x 2 or n x 1 numpy arrays): extracted persistence diagrams. + list of n x 2 or n x 1 numpy arrays: extracted persistence diagrams. """ if self.use: Xfit, num_diag = [], len(X) diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index c1824ebb..91f1bc31 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -58,7 +58,7 @@ class PersistenceImage(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**)): output persistence images. + numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**): output persistence images. """ num_diag, Xfit = len(X), [] new_X = BirthPersistenceTransform().fit_transform(X) @@ -118,7 +118,7 @@ class Landscape(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**)): output persistence landscapes. + numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**): output persistence landscapes. """ num_diag, Xfit = len(X), [] x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) @@ -200,7 +200,7 @@ class Silhouette(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. + numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. """ num_diag, Xfit = len(X), [] x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) @@ -277,7 +277,7 @@ class BettiCurve(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. + numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. """ num_diag, Xfit = len(X), [] x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) @@ -339,7 +339,7 @@ class Entropy(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**)): output entropy. + numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**): output entropy. """ num_diag, Xfit = len(X), [] x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) @@ -402,7 +402,7 @@ class TopologicalVector(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. + numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. """ if self.threshold == -1: thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() @@ -456,7 +456,7 @@ class ComplexPolynomial(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. Returns: - Xfit (numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. + numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. """ if self.threshold == -1: thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() -- cgit v1.2.3 From a043bee376c25254bd413c5579e9611aa57dbf47 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 14:07:06 +0100 Subject: Attributes -> Parameters in __init__ --- src/python/gudhi/sktda/kernel_methods.py | 8 ++++---- src/python/gudhi/sktda/metrics.py | 6 +++--- src/python/gudhi/sktda/preprocessing.py | 10 +++++----- src/python/gudhi/sktda/vector_methods.py | 14 +++++++------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py index e93138e6..c855d2be 100644 --- a/src/python/gudhi/sktda/kernel_methods.py +++ b/src/python/gudhi/sktda/kernel_methods.py @@ -24,7 +24,7 @@ class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): """ Constructor for the SlicedWassersteinKernel class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). """ @@ -62,7 +62,7 @@ class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): """ Constructor for the PersistenceWeightedGaussianKernel class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). @@ -128,7 +128,7 @@ class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): """ Constructor for the PersistenceScaleSpaceKernel class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). """ @@ -173,7 +173,7 @@ class PersistenceFisherKernel(BaseEstimator, TransformerMixin): """ Constructor for the PersistenceFisherKernel class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py index c51b8f3b..c512cb82 100644 --- a/src/python/gudhi/sktda/metrics.py +++ b/src/python/gudhi/sktda/metrics.py @@ -29,7 +29,7 @@ class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): """ Constructor for the SlicedWassersteinDistance class. - Attributes: + Parameters: num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the distance computation (default 10). """ self.num_directions = num_directions @@ -90,7 +90,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): """ Constructor for the BottleneckDistance class. - Attributes: + Parameters: epsilon (double): approximation quality (default 1e-4). """ self.epsilon = epsilon @@ -152,7 +152,7 @@ class PersistenceFisherDistance(BaseEstimator, TransformerMixin): """ Constructor for the PersistenceFisherDistance class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). """ diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py index 294b5aeb..83227ca1 100644 --- a/src/python/gudhi/sktda/preprocessing.py +++ b/src/python/gudhi/sktda/preprocessing.py @@ -62,7 +62,7 @@ class Clamping(BaseEstimator, TransformerMixin): """ Constructor for the Clamping class. - Attributes: + Parameters: limit (double): clamping value (default np.inf). """ self.limit = limit @@ -99,7 +99,7 @@ class DiagramScaler(BaseEstimator, TransformerMixin): """ Constructor for the DiagramScaler class. - Attributes: + Parameters: use (bool): whether to use the class or not (default False). scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). """ @@ -150,7 +150,7 @@ class Padding(BaseEstimator, TransformerMixin): """ Constructor for the Padding class. - Attributes: + Parameters: use (bool): whether to use the class or not (default False). """ self.use = use @@ -194,7 +194,7 @@ class ProminentPoints(BaseEstimator, TransformerMixin): """ Constructor for the ProminentPoints class. - Attributes: + Parameters: use (bool): whether to use the class or not (default False). location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. @@ -267,7 +267,7 @@ class DiagramSelector(BaseEstimator, TransformerMixin): """ Constructor for the DiagramSelector class. - Attributes: + Parameters: use (bool): whether to use the class or not (default False). limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py index 91f1bc31..bf32f18e 100644 --- a/src/python/gudhi/sktda/vector_methods.py +++ b/src/python/gudhi/sktda/vector_methods.py @@ -26,7 +26,7 @@ class PersistenceImage(BaseEstimator, TransformerMixin): """ Constructor for the PersistenceImage class. - Attributes: + Parameters: bandwidth (double): bandwidth of the Gaussian kernel (default 1.). weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). @@ -89,7 +89,7 @@ class Landscape(BaseEstimator, TransformerMixin): """ Constructor for the Landscape class. - Attributes: + Parameters: num_landscapes (int): number of piecewise-linear functions to output (default 5). resolution (int): number of sample for all piecewise-linear functions (default 100). sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. @@ -171,7 +171,7 @@ class Silhouette(BaseEstimator, TransformerMixin): """ Constructor for the Silhouette class. - Attributes: + Parameters: weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. resolution (int): number of samples for the weighted average (default 100). sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. @@ -249,7 +249,7 @@ class BettiCurve(BaseEstimator, TransformerMixin): """ Constructor for the BettiCurve class. - Attributes: + Parameters: resolution (int): number of sample for the piecewise-constant function (default 100). sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ @@ -309,7 +309,7 @@ class Entropy(BaseEstimator, TransformerMixin): """ Constructor for the Entropy class. - Attributes: + Parameters: mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". @@ -379,7 +379,7 @@ class TopologicalVector(BaseEstimator, TransformerMixin): """ Constructor for the TopologicalVector class. - Attributes: + Parameters: threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. """ self.threshold = threshold @@ -432,7 +432,7 @@ class ComplexPolynomial(BaseEstimator, TransformerMixin): """ Constructor for the ComplexPolynomial class. - Attributes: + Parameters: polynomial_type (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients, i.e. the number of coefficients corresponding to the largest degree terms of the polynomial. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. """ -- cgit v1.2.3 From b2d81dd8ee2ed7e1269eb16816f9af6794305046 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 15 Nov 2019 22:23:21 +0100 Subject: Run example as a test, without graphics --- src/python/CMakeLists.txt | 5 + .../diagram_vectorizations_distances_kernels.py | 133 +++++++++++++++++++++ src/python/example/ex_diagrams.py | 133 --------------------- src/python/test/test_representations.py | 11 ++ 4 files changed, 149 insertions(+), 133 deletions(-) create mode 100755 src/python/example/diagram_vectorizations_distances_kernels.py delete mode 100755 src/python/example/ex_diagrams.py create mode 100755 src/python/test/test_representations.py diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 5e2207f0..d131b920 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -387,6 +387,11 @@ if(PYTHONINTERP_FOUND) add_gudhi_py_test(test_wasserstein_distance) endif(OT_FOUND) + # Representations + if(SKLEARN_FOUND) + add_gudhi_py_test(test_representations) + endif(SKLEARN_FOUND) + # Documentation generation is available through sphinx - requires all modules if(SPHINX_PATH) if(MATPLOTLIB_FOUND) diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py new file mode 100755 index 00000000..a6a36b7c --- /dev/null +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python + +import matplotlib.pyplot as plt +import numpy as np +from sklearn.kernel_approximation import RBFSampler +from sklearn.preprocessing import MinMaxScaler + +from gudhi.sktda import DiagramSelector, Clamping, Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ + TopologicalVector, DiagramScaler, BirthPersistenceTransform,\ + PersistenceImage, PersistenceWeightedGaussianKernel, Entropy, \ + PersistenceScaleSpaceKernel, SlicedWassersteinDistance,\ + SlicedWassersteinKernel, BottleneckDistance, PersistenceFisherKernel + +D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.], [0., np.inf], [5., np.inf]]) +diags = [D] + +diags = DiagramSelector(use=True, point_type="finite").fit_transform(diags) +diags = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags) +diags = DiagramScaler(use=True, scalers=[([1], Clamping(limit=.9))]).fit_transform(diags) + +D = diags[0] +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,1.],[0.,1.]) +plt.title("Test Persistence Diagram for vector methods") +plt.show() + +LS = Landscape(resolution=1000) +L = LS.fit_transform(diags) +plt.plot(L[0][:1000]) +plt.plot(L[0][1000:2000]) +plt.plot(L[0][2000:3000]) +plt.title("Landscape") +plt.show() + +def pow(n): + return lambda x: np.power(x[1]-x[0],n) + +SH = Silhouette(resolution=1000, weight=pow(2)) +sh = SH.fit_transform(diags) +plt.plot(sh[0]) +plt.title("Silhouette") +plt.show() + +BC = BettiCurve(resolution=1000) +bc = BC.fit_transform(diags) +plt.plot(bc[0]) +plt.title("Betti Curve") +plt.show() + +CP = ComplexPolynomial(threshold=-1, polynomial_type="T") +cp = CP.fit_transform(diags) +print("Complex polynomial is " + str(cp[0,:])) + +TV = TopologicalVector(threshold=-1) +tv = TV.fit_transform(diags) +print("Topological vector is " + str(tv[0,:])) + +PI = PersistenceImage(bandwidth=.1, weight=lambda x: x[1], im_range=[0,1,0,1], resolution=[100,100]) +pi = PI.fit_transform(diags) +plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) +plt.title("Persistence Image") +plt.show() + +ET = Entropy(mode="scalar") +et = ET.fit_transform(diags) +print("Entropy statistic is " + str(et[0,:])) + +ET = Entropy(mode="vector", normalized=False) +et = ET.fit_transform(diags) +plt.plot(et[0]) +plt.title("Entropy function") +plt.show() + +D = np.array([[1.,5.],[3.,6.],[2.,7.]]) +diags2 = [D] + +diags2 = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags2) + +D = diags[0] +plt.scatter(D[:,0],D[:,1]) +D = diags2[0] +plt.scatter(D[:,0],D[:,1]) +plt.plot([0.,1.],[0.,1.]) +plt.title("Test Persistence Diagrams for kernel methods") +plt.show() + +def arctan(C,p): + return lambda x: C*np.arctan(np.power(x[1], p)) + +PWG = PersistenceWeightedGaussianKernel(bandwidth=1., kernel_approx=None, weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("PWG kernel is " + str(Y[0][0])) + +PWG = PersistenceWeightedGaussianKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2])), weight=arctan(1.,1.)) +X = PWG.fit(diags) +Y = PWG.transform(diags2) +print("Approximate PWG kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(bandwidth=1.) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("PSS kernel is " + str(Y[0][0])) + +PSS = PersistenceScaleSpaceKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PSS.fit(diags) +Y = PSS.transform(diags2) +print("Approximate PSS kernel is " + str(Y[0][0])) + +sW = SlicedWassersteinDistance(num_directions=100) +X = sW.fit(diags) +Y = sW.transform(diags2) +print("SW distance is " + str(Y[0][0])) + +SW = SlicedWassersteinKernel(num_directions=100, bandwidth=1.) +X = SW.fit(diags) +Y = SW.transform(diags2) +print("SW kernel is " + str(Y[0][0])) + +W = BottleneckDistance(epsilon=.001) +X = W.fit(diags) +Y = W.transform(diags2) +print("Bottleneck distance is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1.) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("PF kernel is " + str(Y[0][0])) + +PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1., kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) +X = PF.fit(diags) +Y = PF.transform(diags2) +print("Approximate PF kernel is " + str(Y[0][0])) diff --git a/src/python/example/ex_diagrams.py b/src/python/example/ex_diagrams.py deleted file mode 100755 index a6a36b7c..00000000 --- a/src/python/example/ex_diagrams.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python - -import matplotlib.pyplot as plt -import numpy as np -from sklearn.kernel_approximation import RBFSampler -from sklearn.preprocessing import MinMaxScaler - -from gudhi.sktda import DiagramSelector, Clamping, Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ - TopologicalVector, DiagramScaler, BirthPersistenceTransform,\ - PersistenceImage, PersistenceWeightedGaussianKernel, Entropy, \ - PersistenceScaleSpaceKernel, SlicedWassersteinDistance,\ - SlicedWassersteinKernel, BottleneckDistance, PersistenceFisherKernel - -D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.], [0., np.inf], [5., np.inf]]) -diags = [D] - -diags = DiagramSelector(use=True, point_type="finite").fit_transform(diags) -diags = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags) -diags = DiagramScaler(use=True, scalers=[([1], Clamping(limit=.9))]).fit_transform(diags) - -D = diags[0] -plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,1.],[0.,1.]) -plt.title("Test Persistence Diagram for vector methods") -plt.show() - -LS = Landscape(resolution=1000) -L = LS.fit_transform(diags) -plt.plot(L[0][:1000]) -plt.plot(L[0][1000:2000]) -plt.plot(L[0][2000:3000]) -plt.title("Landscape") -plt.show() - -def pow(n): - return lambda x: np.power(x[1]-x[0],n) - -SH = Silhouette(resolution=1000, weight=pow(2)) -sh = SH.fit_transform(diags) -plt.plot(sh[0]) -plt.title("Silhouette") -plt.show() - -BC = BettiCurve(resolution=1000) -bc = BC.fit_transform(diags) -plt.plot(bc[0]) -plt.title("Betti Curve") -plt.show() - -CP = ComplexPolynomial(threshold=-1, polynomial_type="T") -cp = CP.fit_transform(diags) -print("Complex polynomial is " + str(cp[0,:])) - -TV = TopologicalVector(threshold=-1) -tv = TV.fit_transform(diags) -print("Topological vector is " + str(tv[0,:])) - -PI = PersistenceImage(bandwidth=.1, weight=lambda x: x[1], im_range=[0,1,0,1], resolution=[100,100]) -pi = PI.fit_transform(diags) -plt.imshow(np.flip(np.reshape(pi[0], [100,100]), 0)) -plt.title("Persistence Image") -plt.show() - -ET = Entropy(mode="scalar") -et = ET.fit_transform(diags) -print("Entropy statistic is " + str(et[0,:])) - -ET = Entropy(mode="vector", normalized=False) -et = ET.fit_transform(diags) -plt.plot(et[0]) -plt.title("Entropy function") -plt.show() - -D = np.array([[1.,5.],[3.,6.],[2.,7.]]) -diags2 = [D] - -diags2 = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags2) - -D = diags[0] -plt.scatter(D[:,0],D[:,1]) -D = diags2[0] -plt.scatter(D[:,0],D[:,1]) -plt.plot([0.,1.],[0.,1.]) -plt.title("Test Persistence Diagrams for kernel methods") -plt.show() - -def arctan(C,p): - return lambda x: C*np.arctan(np.power(x[1], p)) - -PWG = PersistenceWeightedGaussianKernel(bandwidth=1., kernel_approx=None, weight=arctan(1.,1.)) -X = PWG.fit(diags) -Y = PWG.transform(diags2) -print("PWG kernel is " + str(Y[0][0])) - -PWG = PersistenceWeightedGaussianKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2])), weight=arctan(1.,1.)) -X = PWG.fit(diags) -Y = PWG.transform(diags2) -print("Approximate PWG kernel is " + str(Y[0][0])) - -PSS = PersistenceScaleSpaceKernel(bandwidth=1.) -X = PSS.fit(diags) -Y = PSS.transform(diags2) -print("PSS kernel is " + str(Y[0][0])) - -PSS = PersistenceScaleSpaceKernel(kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) -X = PSS.fit(diags) -Y = PSS.transform(diags2) -print("Approximate PSS kernel is " + str(Y[0][0])) - -sW = SlicedWassersteinDistance(num_directions=100) -X = sW.fit(diags) -Y = sW.transform(diags2) -print("SW distance is " + str(Y[0][0])) - -SW = SlicedWassersteinKernel(num_directions=100, bandwidth=1.) -X = SW.fit(diags) -Y = SW.transform(diags2) -print("SW kernel is " + str(Y[0][0])) - -W = BottleneckDistance(epsilon=.001) -X = W.fit(diags) -Y = W.transform(diags2) -print("Bottleneck distance is " + str(Y[0][0])) - -PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1.) -X = PF.fit(diags) -Y = PF.transform(diags2) -print("PF kernel is " + str(Y[0][0])) - -PF = PersistenceFisherKernel(bandwidth_fisher=1., bandwidth=1., kernel_approx=RBFSampler(gamma=1./2, n_components=100000).fit(np.ones([1,2]))) -X = PF.fit(diags) -Y = PF.transform(diags2) -print("Approximate PF kernel is " + str(Y[0][0])) diff --git a/src/python/test/test_representations.py b/src/python/test/test_representations.py new file mode 100755 index 00000000..4ff65f98 --- /dev/null +++ b/src/python/test/test_representations.py @@ -0,0 +1,11 @@ +import os +import sys +import matplotlib.pyplot as plt +# Disable graphics for testing purposes +plt.show = lambda:None +here = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(here + "/../example") +import diagram_vectorizations_distances_kernels +# pytest is unhappy if there are 0 tests +def test_nothing(): + return None -- cgit v1.2.3 From 908679b72c215d1914d8e3956126fa44367b937f Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 15 Nov 2019 22:45:15 +0100 Subject: The big rename: sktda -> representations --- src/python/CMakeLists.txt | 4 +- src/python/doc/index.rst | 2 +- src/python/doc/representations.rst | 35 ++ src/python/doc/representations_sum.inc | 14 + src/python/doc/sktda.rst | 35 -- src/python/doc/sktda_sum.inc | 14 - .../diagram_vectorizations_distances_kernels.py | 2 +- src/python/gudhi/representations/__init__.py | 6 + src/python/gudhi/representations/kernel_methods.py | 206 +++++++++ src/python/gudhi/representations/metrics.py | 243 +++++++++++ src/python/gudhi/representations/preprocessing.py | 305 +++++++++++++ src/python/gudhi/representations/vector_methods.py | 485 +++++++++++++++++++++ src/python/gudhi/sktda/__init__.py | 6 - src/python/gudhi/sktda/kernel_methods.py | 206 --------- src/python/gudhi/sktda/metrics.py | 243 ----------- src/python/gudhi/sktda/preprocessing.py | 305 ------------- src/python/gudhi/sktda/vector_methods.py | 485 --------------------- 17 files changed, 1298 insertions(+), 1298 deletions(-) create mode 100644 src/python/doc/representations.rst create mode 100644 src/python/doc/representations_sum.inc delete mode 100644 src/python/doc/sktda.rst delete mode 100644 src/python/doc/sktda_sum.inc create mode 100644 src/python/gudhi/representations/__init__.py create mode 100644 src/python/gudhi/representations/kernel_methods.py create mode 100644 src/python/gudhi/representations/metrics.py create mode 100644 src/python/gudhi/representations/preprocessing.py create mode 100644 src/python/gudhi/representations/vector_methods.py delete mode 100644 src/python/gudhi/sktda/__init__.py delete mode 100644 src/python/gudhi/sktda/kernel_methods.py delete mode 100644 src/python/gudhi/sktda/metrics.py delete mode 100644 src/python/gudhi/sktda/preprocessing.py delete mode 100644 src/python/gudhi/sktda/vector_methods.py diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index d131b920..2c568b66 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -50,7 +50,7 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES "${GUDHI_PYTHON_MODULES}'euclidean_strong_witness_complex', ") # Modules that should not be auto-imported in __init__.py - set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'sktda', ") + set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'representations', ") set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ") add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") @@ -208,7 +208,7 @@ if(PYTHONINTERP_FOUND) # Other .py files file(COPY "gudhi/persistence_graphical_tools.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") - file(COPY "gudhi/sktda" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/") + file(COPY "gudhi/representations" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/") file(COPY "gudhi/wasserstein.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") add_custom_command( diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index f8dd3c2f..1ef08096 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -81,7 +81,7 @@ Wasserstein distance Persistence representations =========================== -.. include:: sktda_sum.inc +.. include:: representations_sum.inc Persistence graphical tools =========================== diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst new file mode 100644 index 00000000..3db1c95a --- /dev/null +++ b/src/python/doc/representations.rst @@ -0,0 +1,35 @@ +:orphan: + +.. To get rid of WARNING: document isn't included in any toctree + +=================================== +Representations reference manual +=================================== + +Preprocessing +------------- +.. automodule:: gudhi.representations.preprocessing + :members: + :special-members: + :show-inheritance: + +Vector methods +-------------- +.. automodule:: gudhi.representations.vector_methods + :members: + :special-members: + :show-inheritance: + +Kernel methods +-------------- +.. automodule:: gudhi.representations.kernel_methods + :members: + :special-members: + :show-inheritance: + +Metrics +------- +.. automodule:: gudhi.representations.metrics + :members: + :special-members: + :show-inheritance: diff --git a/src/python/doc/representations_sum.inc b/src/python/doc/representations_sum.inc new file mode 100644 index 00000000..7b167a17 --- /dev/null +++ b/src/python/doc/representations_sum.inc @@ -0,0 +1,14 @@ +.. table:: + :widths: 30 50 20 + + +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ + | .. figure:: | Vectorizations, distances and kernels that work on persistence | :Author: Mathieu Carrière | + | ../../doc/Persistence_representations/average_landscape.png | diagrams, compatible with scikit-learn. | | + | | | :Introduced in: GUDHI 3.1.0 | + | | | | + | | | :Copyright: MIT | + | | | | + | | | :Requires: scikit-learn | + +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ + | * :doc:`representations` | + +------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+ diff --git a/src/python/doc/sktda.rst b/src/python/doc/sktda.rst deleted file mode 100644 index 9acec616..00000000 --- a/src/python/doc/sktda.rst +++ /dev/null @@ -1,35 +0,0 @@ -:orphan: - -.. To get rid of WARNING: document isn't included in any toctree - -=================================== -sktda reference manual -=================================== - -Preprocessing -------------- -.. automodule:: gudhi.sktda.preprocessing - :members: - :special-members: - :show-inheritance: - -Vector methods --------------- -.. automodule:: gudhi.sktda.vector_methods - :members: - :special-members: - :show-inheritance: - -Kernel methods --------------- -.. automodule:: gudhi.sktda.kernel_methods - :members: - :special-members: - :show-inheritance: - -Metrics -------- -.. automodule:: gudhi.sktda.metrics - :members: - :special-members: - :show-inheritance: diff --git a/src/python/doc/sktda_sum.inc b/src/python/doc/sktda_sum.inc deleted file mode 100644 index a0f77e7e..00000000 --- a/src/python/doc/sktda_sum.inc +++ /dev/null @@ -1,14 +0,0 @@ -.. table:: - :widths: 30 50 20 - - +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ - | .. figure:: | Vectorizations, distances and kernels that work on persistence | :Author: Mathieu Carrière | - | ../../doc/Persistence_representations/average_landscape.png | diagrams, compatible with scikit-learn. | | - | | | :Introduced in: GUDHI 3.1.0 | - | | | | - | | | :Copyright: MIT | - | | | | - | | | :Requires: scikit-learn | - +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ - | * :doc:`sktda` | - +------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+ diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py index a6a36b7c..a77bbfdd 100755 --- a/src/python/example/diagram_vectorizations_distances_kernels.py +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -5,7 +5,7 @@ import numpy as np from sklearn.kernel_approximation import RBFSampler from sklearn.preprocessing import MinMaxScaler -from gudhi.sktda import DiagramSelector, Clamping, Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ +from gudhi.representations import DiagramSelector, Clamping, Landscape, Silhouette, BettiCurve, ComplexPolynomial,\ TopologicalVector, DiagramScaler, BirthPersistenceTransform,\ PersistenceImage, PersistenceWeightedGaussianKernel, Entropy, \ PersistenceScaleSpaceKernel, SlicedWassersteinDistance,\ diff --git a/src/python/gudhi/representations/__init__.py b/src/python/gudhi/representations/__init__.py new file mode 100644 index 00000000..f020248d --- /dev/null +++ b/src/python/gudhi/representations/__init__.py @@ -0,0 +1,6 @@ +from .kernel_methods import * +from .metrics import * +from .preprocessing import * +from .vector_methods import * + +__all__ = ["kernel_methods", "metrics", "preprocessing", "vector_methods"] diff --git a/src/python/gudhi/representations/kernel_methods.py b/src/python/gudhi/representations/kernel_methods.py new file mode 100644 index 00000000..c855d2be --- /dev/null +++ b/src/python/gudhi/representations/kernel_methods.py @@ -0,0 +1,206 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +from .metrics import SlicedWassersteinDistance, PersistenceFisherDistance + +############################################# +# Kernel methods ############################ +############################################# + +class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein kernel matrix from a list of persistence diagrams. The sliced Wasserstein kernel is computed by exponentiating the corresponding sliced Wasserstein distance with a Gaussian kernel. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10, bandwidth=1.0): + """ + Constructor for the SlicedWassersteinKernel class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). + num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). + """ + self.bandwidth = bandwidth + self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinKernel class on a list of persistence diagrams: an instance of the SlicedWassersteinDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.sw_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein kernel values. + """ + return np.exp(-self.sw_.transform(X)/self.bandwidth) + +class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence weighted Gaussian kernel matrix from a list of persistence diagrams. The persistence weighted Gaussian kernel is computed by convolving the persistence diagram points with weighted Gaussian kernels. See http://proceedings.mlr.press/v48/kusano16.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, kernel_approx=None): + """ + Constructor for the PersistenceWeightedGaussianKernel class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.weight = bandwidth, weight + self.kernel_approx = kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceWeightedGaussianKernel class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + self.ws_ = [ np.array([self.weight(self.diagrams_[i][j,:]) for j in range(self.diagrams_[i].shape[0])]) for i in range(len(self.diagrams_)) ] + if self.kernel_approx is not None: + self.approx_ = np.concatenate([np.sum(np.multiply(self.ws_[i][:,np.newaxis], self.kernel_approx.transform(self.diagrams_[i])), axis=0)[np.newaxis,:] for i in range(len(self.diagrams_))]) + return self + + def transform(self, X): + """ + Compute all persistence weighted Gaussian kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence weighted Gaussian kernel values. + """ + Xp = list(X) + Xfit = np.zeros((len(Xp), len(self.diagrams_))) + if len(self.diagrams_) == len(Xp) and np.all([np.array_equal(self.diagrams_[i], Xp[i]) for i in range(len(Xp))]): + if self.kernel_approx is not None: + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(self.approx_, self.approx_.T) + else: + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + Xfit[j,i] = Xfit[i,j] + else: + ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] + if self.kernel_approx is not None: + approx = np.concatenate([np.sum(np.multiply(ws[i][:,np.newaxis], self.kernel_approx.transform(Xp[i])), axis=0)[np.newaxis,:] for i in range(len(Xp))]) + Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(approx, self.approx_.T) + else: + for i in range(len(Xp)): + for j in range(len(self.diagrams_)): + W = np.matmul(ws[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) + E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(Xp[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) + Xfit[i,j] = np.sum(np.multiply(W, E)) + + return Xfit + +class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, with negative weight, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceScaleSpaceKernel class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.pwg_ = PersistenceWeightedGaussianKernel(bandwidth=bandwidth, weight=lambda x: 1 if x[1] >= x[0] else -1, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceScaleSpaceKernel class on a list of persistence diagrams: symmetric to the diagonal of all points are computed and an instance of the PersistenceWeightedGaussianKernel class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = list(X) + for i in range(len(self.diagrams_)): + op_D = self.diagrams_[i][:,[1,0]] + self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) + self.pwg_.fit(X) + return self + + def transform(self, X): + """ + Compute all persistence scale space kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence scale space kernel values. + """ + Xp = list(X) + for i in range(len(Xp)): + op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) + Xp[i] = np.concatenate([Xp[i], op_X], axis=0) + return self.pwg_.transform(Xp) + +class PersistenceFisherKernel(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher kernel matrix from a list of persistence diagrams. The persistence Fisher kernel is computed by exponentiating the corresponding persistence Fisher distance with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth_fisher=1., bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherKernel class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). + bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth = bandwidth + self.pf_ = PersistenceFisherDistance(bandwidth=bandwidth_fisher, kernel_approx=kernel_approx) + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherKernel class on a list of persistence diagrams: an instance of the PersistenceFisherDistance class is fitted on the diagrams and then stored. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.pf_.fit(X, y) + return self + + def transform(self, X): + """ + Compute all persistence Fisher kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher kernel values. + """ + return np.exp(-self.pf_.transform(X)/self.bandwidth) + diff --git a/src/python/gudhi/representations/metrics.py b/src/python/gudhi/representations/metrics.py new file mode 100644 index 00000000..c512cb82 --- /dev/null +++ b/src/python/gudhi/representations/metrics.py @@ -0,0 +1,243 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.metrics import pairwise_distances +try: + from .. import bottleneck_distance + USE_GUDHI = True +except ImportError: + USE_GUDHI = False + print("Gudhi built without CGAL: BottleneckDistance will return a null matrix") + +############################################# +# Metrics ################################### +############################################# + +class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the sliced Wasserstein distance matrix from a list of persistence diagrams. The Sliced Wasserstein distance is computed by projecting the persistence diagrams onto lines, comparing the projections with the 1-norm, and finally integrating over all possible lines. See http://proceedings.mlr.press/v70/carriere17a.html for more details. + """ + def __init__(self, num_directions=10): + """ + Constructor for the SlicedWassersteinDistance class. + + Parameters: + num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the distance computation (default 10). + """ + self.num_directions = num_directions + thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] + self.lines_ = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=0) + + def fit(self, X, y=None): + """ + Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in numpy arrays, called **diagrams_** and **approx_diag_**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + self.approx_ = [np.matmul(X[i], self.lines_) for i in range(len(X))] + diag_proj = (1./2) * np.ones((2,2)) + self.approx_diag_ = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all sliced Wasserstein distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein distances. + """ + Xfit = np.zeros((len(X), len(self.approx_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.approx_)): + for j in range(i+1, len(self.approx_)): + A = np.sort(np.concatenate([self.approx_[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], self.approx_diag_[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + Xfit[j,i] = Xfit[i,j] + else: + diag_proj = (1./2) * np.ones((2,2)) + approx = [np.matmul(X[i], self.lines_) for i in range(len(X))] + approx_diag = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] + for i in range(len(approx)): + for j in range(len(self.approx_)): + A = np.sort(np.concatenate([approx[i], self.approx_diag_[j]], axis=0), axis=0) + B = np.sort(np.concatenate([self.approx_[j], approx_diag[i]], axis=0), axis=0) + L1 = np.sum(np.abs(A-B), axis=0) + Xfit[i,j] = np.mean(L1) + + return Xfit + +class BottleneckDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. + """ + def __init__(self, epsilon=1e-3): + """ + Constructor for the BottleneckDistance class. + + Parameters: + epsilon (double): approximation quality (default 1e-4). + """ + self.epsilon = epsilon + + def fit(self, X, y=None): + """ + Fit the BottleneckDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams**. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + return self + + def transform(self, X): + """ + Compute all bottleneck distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise bottleneck distances. + """ + num_diag1 = len(X) + + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + matrix = np.zeros((num_diag1, num_diag1)) + + if USE_GUDHI: + for i in range(num_diag1): + for j in range(i+1, num_diag1): + matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) + matrix[j,i] = matrix[i,j] + else: + print("Gudhi required---returning null matrix") + + else: + num_diag2 = len(self.diagrams_) + matrix = np.zeros((num_diag1, num_diag2)) + + if USE_GUDHI: + for i in range(num_diag1): + for j in range(num_diag2): + matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) + else: + print("Gudhi required---returning null matrix") + + Xfit = matrix + + return Xfit + +class PersistenceFisherDistance(BaseEstimator, TransformerMixin): + """ + This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See http://papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. + """ + def __init__(self, bandwidth=1., kernel_approx=None): + """ + Constructor for the PersistenceFisherDistance class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). + kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). + """ + self.bandwidth, self.kernel_approx = bandwidth, kernel_approx + + def fit(self, X, y=None): + """ + Fit the PersistenceFisherDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.diagrams_ = X + projection = (1./2) * np.ones((2,2)) + self.diagonal_projections_ = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + self.approx_ = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + self.approx_diagonal_ = [self.kernel_approx.transform(self.diagonal_projections_[i]) for i in range(len(X))] + return self + + def transform(self, X): + """ + Compute all persistence Fisher distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher distances. + """ + Xfit = np.zeros((len(X), len(self.diagrams_))) + if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + for i in range(len(self.diagrams_)): + for j in range(i+1, len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + Xfit[j,i] = Xfit[i,j] + else: + Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + Xfit[j,i] = Xfit[i,j] + else: + projection = (1./2) * np.ones((2,2)) + diagonal_projections = [np.matmul(X[i], projection) for i in range(len(X))] + if self.kernel_approx is not None: + approx = [self.kernel_approx.transform(X[i]) for i in range(len(X))] + approx_diagonal = [self.kernel_approx.transform(diagonal_projections[i]) for i in range(len(X))] + for i in range(len(X)): + for j in range(len(self.diagrams_)): + if self.kernel_approx is not None: + Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) + U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) + vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + else: + Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) + U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) + vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) + vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) + if vectori_sum != 0: + vectori = vectori/vectori_sum + if vectorj_sum != 0: + vectorj = vectorj/vectorj_sum + Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) + return Xfit diff --git a/src/python/gudhi/representations/preprocessing.py b/src/python/gudhi/representations/preprocessing.py new file mode 100644 index 00000000..83227ca1 --- /dev/null +++ b/src/python/gudhi/representations/preprocessing.py @@ -0,0 +1,305 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import StandardScaler + +############################################# +# Preprocessing ############################# +############################################# + +class BirthPersistenceTransform(BaseEstimator, TransformerMixin): + """ + This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. + """ + def __init__(self): + """ + Constructor for BirthPersistenceTransform class. + """ + return None + + def fit(self, X, y=None): + """ + Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). + + Parameters: + X (n x 2 numpy array): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Apply the BirthPersistenceTransform function on the persistence diagrams. + + Parameters: + X (list of n x 2 numpy array): input persistence diagrams. + + Returns: + list of n x 2 numpy array: transformed persistence diagrams. + """ + Xfit = [] + for diag in X: + #new_diag = np.empty(diag.shape) + #np.copyto(new_diag, diag) + new_diag = np.copy(diag) + new_diag[:,1] = new_diag[:,1] - new_diag[:,0] + Xfit.append(new_diag) + return Xfit + +class Clamping(BaseEstimator, TransformerMixin): + """ + This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. + """ + def __init__(self, limit=np.inf): + """ + Constructor for the Clamping class. + + Parameters: + limit (double): clamping value (default np.inf). + """ + self.limit = limit + + def fit(self, X, y=None): + """ + Fit the Clamping class on a list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). + + Parameters: + X (numpy array of size n): input values. + y (n x 1 array): value labels (unused). + """ + return self + + def transform(self, X): + """ + Clamp list of values. + + Parameters: + X (numpy array of size n): input list of values. + + Returns: + numpy array of size n: output list of values. + """ + Xfit = np.minimum(X, self.limit) + #Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) + return Xfit + +class DiagramScaler(BaseEstimator, TransformerMixin): + """ + This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. + """ + def __init__(self, use=False, scalers=[]): + """ + Constructor for the DiagramScaler class. + + Parameters: + use (bool): whether to use the class or not (default False). + scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). + """ + self.scalers = scalers + self.use = use + + def fit(self, X, y=None): + """ + Fit the DiagramScaler class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if self.use: + if len(X) == 1: + P = X[0] + else: + P = np.concatenate(X,0) + for (indices, scaler) in self.scalers: + scaler.fit(np.reshape(P[:,indices], [-1, 1])) + return self + + def transform(self, X): + """ + Apply the DiagramScaler function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + list of n x 2 or n x 1 numpy arrays: transformed persistence diagrams. + """ + Xfit = [np.copy(d) for d in X] + if self.use: + for i in range(len(Xfit)): + if Xfit[i].shape[0] > 0: + for (indices, scaler) in self.scalers: + for I in indices: + Xfit[i][:,I] = np.squeeze(scaler.transform(np.reshape(Xfit[i][:,I], [-1,1]))) + return Xfit + +class Padding(BaseEstimator, TransformerMixin): + """ + This is a class for padding a list of persistence diagrams with dummy points, so that all persistence diagrams end up with the same number of points. + """ + def __init__(self, use=False): + """ + Constructor for the Padding class. + + Parameters: + use (bool): whether to use the class or not (default False). + """ + self.use = use + + def fit(self, X, y=None): + """ + Fit the Padding class on a list of persistence diagrams (this function actually does nothing but is useful when Padding is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + self.max_pts = max([len(diag) for diag in X]) + return self + + def transform(self, X): + """ + Add dummy points to each persistence diagram so that they all have the same cardinality. All points are given an additional coordinate indicating if the point was added after padding (0) or already present before (1). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + list of n x 3 or n x 2 numpy arrays: padded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + for diag in X: + diag_pad = np.pad(diag, ((0,max(0, self.max_pts - diag.shape[0])), (0,1)), "constant", constant_values=((0,0),(0,0))) + diag_pad[:diag.shape[0],2] = np.ones(diag.shape[0]) + Xfit.append(diag_pad) + else: + Xfit = X + return Xfit + +class ProminentPoints(BaseEstimator, TransformerMixin): + """ + This is a class for removing points that are close or far from the diagonal in persistence diagrams. If persistence diagrams are n x 2 numpy arrays (i.e. persistence diagrams with ordinary features), points are ordered and thresholded by distance-to-diagonal. If persistence diagrams are n x 1 numpy arrays (i.e. persistence diagrams with essential features), points are not ordered and thresholded by first coordinate. + """ + def __init__(self, use=False, num_pts=10, threshold=-1, location="upper"): + """ + Constructor for the ProminentPoints class. + + Parameters: + use (bool): whether to use the class or not (default False). + location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. + num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. + threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. + """ + self.num_pts = num_pts + self.threshold = threshold + self.use = use + self.location = location + + def fit(self, X, y=None): + """ + Fit the ProminentPoints class on a list of persistence diagrams (this function actually does nothing but is useful when ProminentPoints is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + list of n x 2 or n x 1 numpy arrays: thresholded persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + for i in range(num_diag): + diag = X[i] + if diag.shape[1] >= 2: + if diag.shape[0] > 0: + pers = np.abs(diag[:,1] - diag[:,0]) + idx_thresh = pers >= self.threshold + thresh_diag, thresh_pers = diag[idx_thresh], pers[idx_thresh] + sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) + if self.location == "upper": + new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh] ], axis=0) + else: + new_diag = diag + + else: + if diag.shape[0] > 0: + birth = diag[:,:1] + idx_thresh = birth >= self.threshold + thresh_diag, thresh_birth = diag[idx_thresh], birth[idx_thresh] + if self.location == "upper": + new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] + if self.location == "lower": + new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh] ], axis=0) + else: + new_diag = diag + + Xfit.append(new_diag) + else: + Xfit = X + return Xfit + +class DiagramSelector(BaseEstimator, TransformerMixin): + """ + This is a class for extracting finite or essential points in persistence diagrams. + """ + def __init__(self, use=False, limit=np.inf, point_type="finite"): + """ + Constructor for the DiagramSelector class. + + Parameters: + use (bool): whether to use the class or not (default False). + limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). + point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. + """ + self.use, self.limit, self.point_type = use, limit, point_type + + def fit(self, X, y=None): + """ + Fit the DiagramSelector class on a list of persistence diagrams (this function actually does nothing but is useful when DiagramSelector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Extract and return the finite or essential points of each persistence diagram individually. + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + + Returns: + list of n x 2 or n x 1 numpy arrays: extracted persistence diagrams. + """ + if self.use: + Xfit, num_diag = [], len(X) + if self.point_type == "finite": + Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] + else: + Xfit = [ diag[diag[:,1] >= self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] + else: + Xfit = X + return Xfit diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py new file mode 100644 index 00000000..bf32f18e --- /dev/null +++ b/src/python/gudhi/representations/vector_methods.py @@ -0,0 +1,485 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Mathieu Carrière +# +# Copyright (C) 2018-2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + +import numpy as np +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler +from sklearn.neighbors import DistanceMetric + +from .preprocessing import DiagramScaler, BirthPersistenceTransform + +############################################# +# Finite Vectorization methods ############## +############################################# + +class PersistenceImage(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence images from a list of persistence diagrams. A persistence image is a 2D function computed from a persistence diagram by convolving the diagram points with a weighted Gaussian kernel. The plane is then discretized into an image with pixels, which is flattened and returned as a vector. See http://jmlr.org/papers/v18/16-337.html for more details. + """ + def __init__(self, bandwidth=1., weight=lambda x: 1, resolution=[20,20], im_range=[np.nan, np.nan, np.nan, np.nan]): + """ + Constructor for the PersistenceImage class. + + Parameters: + bandwidth (double): bandwidth of the Gaussian kernel (default 1.). + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. + resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). + im_range ([double,double,double,double]): minimum and maximum of each axis of the persistence image, of the form [x_min, x_max, y_min, y_max] (default [numpy.nan, numpy.nan, numpy.nan, numpyp.nan]). If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.bandwidth, self.weight = bandwidth, weight + self.resolution, self.im_range = resolution, im_range + + def fit(self, X, y=None): + """ + Fit the PersistenceImage class on a list of persistence diagrams: if any of the values in **im_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.im_range)).any(): + new_X = BirthPersistenceTransform().fit_transform(X) + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(new_X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) + return self + + def transform(self, X): + """ + Compute the persistence image for each persistence diagram individually and store the results in a single numpy array. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**): output persistence images. + """ + num_diag, Xfit = len(X), [] + new_X = BirthPersistenceTransform().fit_transform(X) + + for i in range(num_diag): + + diagram, num_pts_in_diag = new_X[i], X[i].shape[0] + + w = np.empty(num_pts_in_diag) + for j in range(num_pts_in_diag): + w[j] = self.weight(diagram[j,:]) + + x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) + Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) + image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(np.square(self.bandwidth)*2*np.pi), 1) + + Xfit.append(image.flatten()[np.newaxis,:]) + + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Landscape(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. + """ + def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Landscape class. + + Parameters: + num_landscapes (int): number of piecewise-linear functions to output (default 5). + resolution (int): number of sample for all piecewise-linear functions (default 100). + sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Landscape class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the persistence landscape for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**): output persistence landscapes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + ls = np.zeros([self.num_landscapes, self.resolution]) + + events = [] + for j in range(self.resolution): + events.append([]) + + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + landscape_value = self.sample_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + events[k].append(landscape_value) + landscape_value += step_x + + landscape_value = py - self.sample_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + events[k].append(landscape_value) + landscape_value -= step_x + + for j in range(self.resolution): + events[j].sort(reverse=True) + for k in range( min(self.num_landscapes, len(events[j])) ): + ls[k,j] = events[j][k] + + Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) + + Xfit = np.concatenate(Xfit,0) + + return Xfit + +class Silhouette(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. + """ + def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Silhouette class. + + Parameters: + weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. + resolution (int): number of samples for the weighted average (default 100). + sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.weight, self.resolution, self.sample_range = weight, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the persistence silhouette for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + sh, weights = np.zeros(self.resolution), np.zeros(num_pts_in_diag) + for j in range(num_pts_in_diag): + weights[j] = self.weight(diagram[j,:]) + total_weight = np.sum(weights) + + for j in range(num_pts_in_diag): + + [px,py] = diagram[j,:2] + weight = weights[j] / total_weight + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + + if min_idx < self.resolution and max_idx > 0: + + silhouette_value = self.sample_range[0] + min_idx * step_x - px + for k in range(min_idx, mid_idx): + sh[k] += weight * silhouette_value + silhouette_value += step_x + + silhouette_value = py - self.sample_range[0] - mid_idx * step_x + for k in range(mid_idx, max_idx): + sh[k] += weight * silhouette_value + silhouette_value -= step_x + + Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class BettiCurve(BaseEstimator, TransformerMixin): + """ + This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. + """ + def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the BettiCurve class. + + Parameters: + resolution (int): number of sample for the piecewise-constant function (default 100). + sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + """ + self.resolution, self.sample_range = resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the Betti curve for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + + bc = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + bc[k] += 1 + + Xfit.append(np.reshape(bc,[1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class Entropy(BaseEstimator, TransformerMixin): + """ + This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. Note that a previous implementation was contributed by Manuel Soriano-Trigueros. + """ + def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): + """ + Constructor for the Entropy class. + + Parameters: + mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). + normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". + resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". + sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". + """ + self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range + + def fit(self, X, y=None): + """ + Fit the Entropy class on a list of persistence diagrams. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + if np.isnan(np.array(self.sample_range)).any(): + pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) + [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] + self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + return self + + def transform(self, X): + """ + Compute the entropy for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**): output entropy. + """ + num_diag, Xfit = len(X), [] + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + step_x = x_values[1] - x_values[0] + new_X = BirthPersistenceTransform().fit_transform(X) + + for i in range(num_diag): + + orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] + new_diagram = DiagramScaler(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] + + if self.mode == "scalar": + ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) + Xfit.append(np.array([[ent]])) + + else: + ent = np.zeros(self.resolution) + for j in range(num_pts_in_diag): + [px,py] = orig_diagram[j,:2] + min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + for k in range(min_idx, max_idx): + ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) + if self.normalized: + ent = ent / np.linalg.norm(ent, ord=1) + Xfit.append(np.reshape(ent,[1,-1])) + + Xfit = np.concatenate(Xfit, 0) + + return Xfit + +class TopologicalVector(BaseEstimator, TransformerMixin): + """ + This is a class for computing topological vectors from a list of persistence diagrams. The topological vector associated to a persistence diagram is the sorted vector of a slight modification of the pairwise distances between the persistence diagram points. See https://diglib.eg.org/handle/10.1111/cgf12692 for more details. + """ + def __init__(self, threshold=10): + """ + Constructor for the TopologicalVector class. + + Parameters: + threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. + """ + self.threshold = threshold + + def fit(self, X, y=None): + """ + Fit the TopologicalVector class on a list of persistence diagrams (this function actually does nothing but is useful when TopologicalVector is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the topological vector for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + num_diag = len(X) + Xfit = np.zeros([num_diag, thresh]) + + for i in range(num_diag): + + diagram, num_pts_in_diag = X[i], X[i].shape[0] + pers = 0.5 * (diagram[:,1]-diagram[:,0]) + min_pers = np.minimum(pers,np.transpose(pers)) + distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) + vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) + dim = min(len(vect), thresh) + Xfit[i, :dim] = vect[:dim] + + return Xfit + +class ComplexPolynomial(BaseEstimator, TransformerMixin): + """ + This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. + """ + def __init__(self, polynomial_type="R", threshold=10): + """ + Constructor for the ComplexPolynomial class. + + Parameters: + polynomial_type (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). + threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients, i.e. the number of coefficients corresponding to the largest degree terms of the polynomial. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. + """ + self.threshold, self.polynomial_type = threshold, polynomial_type + + def fit(self, X, y=None): + """ + Fit the ComplexPolynomial class on a list of persistence diagrams (this function actually does nothing but is useful when ComplexPolynomial is included in a scikit-learn Pipeline). + + Parameters: + X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. + y (n x 1 array): persistence diagram labels (unused). + """ + return self + + def transform(self, X): + """ + Compute the complex vector of coefficients for each persistence diagram individually and concatenate the results. + + Parameters: + X (list of n x 2 numpy arrays): input persistence diagrams. + + Returns: + numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. + """ + if self.threshold == -1: + thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() + else: + thresh = self.threshold + + Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) + for d in range(len(X)): + D, N = X[d], X[d].shape[0] + if self.polynomial_type == "R": + roots = D[:,0] + 1j * D[:,1] + elif self.polynomial_type == "S": + alpha = np.linalg.norm(D, axis=1) + alpha = np.where(alpha==0, np.ones(N), alpha) + roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) + elif self.polynomial_type == "T": + alpha = np.linalg.norm(D, axis=1) + roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) + coeff = [0] * (N+1) + coeff[N] = 1 + for i in range(1, N+1): + for j in range(N-i-1, N): + coeff[j] += ((-1) * roots[i-1] * coeff[j+1]) + coeff = np.array(coeff[::-1])[1:] + Xfit[d, :min(thresh, coeff.shape[0])] = coeff[:min(thresh, coeff.shape[0])] + return Xfit diff --git a/src/python/gudhi/sktda/__init__.py b/src/python/gudhi/sktda/__init__.py deleted file mode 100644 index f020248d..00000000 --- a/src/python/gudhi/sktda/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .kernel_methods import * -from .metrics import * -from .preprocessing import * -from .vector_methods import * - -__all__ = ["kernel_methods", "metrics", "preprocessing", "vector_methods"] diff --git a/src/python/gudhi/sktda/kernel_methods.py b/src/python/gudhi/sktda/kernel_methods.py deleted file mode 100644 index c855d2be..00000000 --- a/src/python/gudhi/sktda/kernel_methods.py +++ /dev/null @@ -1,206 +0,0 @@ -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Mathieu Carrière -# -# Copyright (C) 2018-2019 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.metrics import pairwise_distances -from .metrics import SlicedWassersteinDistance, PersistenceFisherDistance - -############################################# -# Kernel methods ############################ -############################################# - -class SlicedWassersteinKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the sliced Wasserstein kernel matrix from a list of persistence diagrams. The sliced Wasserstein kernel is computed by exponentiating the corresponding sliced Wasserstein distance with a Gaussian kernel. See http://proceedings.mlr.press/v70/carriere17a.html for more details. - """ - def __init__(self, num_directions=10, bandwidth=1.0): - """ - Constructor for the SlicedWassersteinKernel class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel applied to the sliced Wasserstein distance (default 1.). - num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the kernel computation (default 10). - """ - self.bandwidth = bandwidth - self.sw_ = SlicedWassersteinDistance(num_directions=num_directions) - - def fit(self, X, y=None): - """ - Fit the SlicedWassersteinKernel class on a list of persistence diagrams: an instance of the SlicedWassersteinDistance class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.sw_.fit(X, y) - return self - - def transform(self, X): - """ - Compute all sliced Wasserstein kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein kernel values. - """ - return np.exp(-self.sw_.transform(X)/self.bandwidth) - -class PersistenceWeightedGaussianKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence weighted Gaussian kernel matrix from a list of persistence diagrams. The persistence weighted Gaussian kernel is computed by convolving the persistence diagram points with weighted Gaussian kernels. See http://proceedings.mlr.press/v48/kusano16.html for more details. - """ - def __init__(self, bandwidth=1., weight=lambda x: 1, kernel_approx=None): - """ - Constructor for the PersistenceWeightedGaussianKernel class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth, self.weight = bandwidth, weight - self.kernel_approx = kernel_approx - - def fit(self, X, y=None): - """ - Fit the PersistenceWeightedGaussianKernel class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = list(X) - self.ws_ = [ np.array([self.weight(self.diagrams_[i][j,:]) for j in range(self.diagrams_[i].shape[0])]) for i in range(len(self.diagrams_)) ] - if self.kernel_approx is not None: - self.approx_ = np.concatenate([np.sum(np.multiply(self.ws_[i][:,np.newaxis], self.kernel_approx.transform(self.diagrams_[i])), axis=0)[np.newaxis,:] for i in range(len(self.diagrams_))]) - return self - - def transform(self, X): - """ - Compute all persistence weighted Gaussian kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence weighted Gaussian kernel values. - """ - Xp = list(X) - Xfit = np.zeros((len(Xp), len(self.diagrams_))) - if len(self.diagrams_) == len(Xp) and np.all([np.array_equal(self.diagrams_[i], Xp[i]) for i in range(len(Xp))]): - if self.kernel_approx is not None: - Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(self.approx_, self.approx_.T) - else: - for i in range(len(self.diagrams_)): - for j in range(i+1, len(self.diagrams_)): - W = np.matmul(self.ws_[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) - E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(self.diagrams_[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) - Xfit[i,j] = np.sum(np.multiply(W, E)) - Xfit[j,i] = Xfit[i,j] - else: - ws = [ np.array([self.weight(Xp[i][j,:]) for j in range(Xp[i].shape[0])]) for i in range(len(Xp)) ] - if self.kernel_approx is not None: - approx = np.concatenate([np.sum(np.multiply(ws[i][:,np.newaxis], self.kernel_approx.transform(Xp[i])), axis=0)[np.newaxis,:] for i in range(len(Xp))]) - Xfit = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.matmul(approx, self.approx_.T) - else: - for i in range(len(Xp)): - for j in range(len(self.diagrams_)): - W = np.matmul(ws[i][:,np.newaxis], self.ws_[j][np.newaxis,:]) - E = (1./(np.sqrt(2*np.pi)*self.bandwidth)) * np.exp(-np.square(pairwise_distances(Xp[i], self.diagrams_[j]))/(2*np.square(self.bandwidth))) - Xfit[i,j] = np.sum(np.multiply(W, E)) - - return Xfit - -class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence scale space kernel matrix from a list of persistence diagrams. The persistence scale space kernel is computed by adding the symmetric to the diagonal of each point in each persistence diagram, with negative weight, and then convolving the points with a Gaussian kernel. See https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Reininghaus_A_Stable_Multi-Scale_2015_CVPR_paper.pdf for more details. - """ - def __init__(self, bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceScaleSpaceKernel class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel with which persistence diagrams will be convolved (default 1.) - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.pwg_ = PersistenceWeightedGaussianKernel(bandwidth=bandwidth, weight=lambda x: 1 if x[1] >= x[0] else -1, kernel_approx=kernel_approx) - - def fit(self, X, y=None): - """ - Fit the PersistenceScaleSpaceKernel class on a list of persistence diagrams: symmetric to the diagonal of all points are computed and an instance of the PersistenceWeightedGaussianKernel class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = list(X) - for i in range(len(self.diagrams_)): - op_D = self.diagrams_[i][:,[1,0]] - self.diagrams_[i] = np.concatenate([self.diagrams_[i], op_D], axis=0) - self.pwg_.fit(X) - return self - - def transform(self, X): - """ - Compute all persistence scale space kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence scale space kernel values. - """ - Xp = list(X) - for i in range(len(Xp)): - op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) - Xp[i] = np.concatenate([Xp[i], op_X], axis=0) - return self.pwg_.transform(Xp) - -class PersistenceFisherKernel(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence Fisher kernel matrix from a list of persistence diagrams. The persistence Fisher kernel is computed by exponentiating the corresponding persistence Fisher distance with a Gaussian kernel. See papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. - """ - def __init__(self, bandwidth_fisher=1., bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceFisherKernel class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel applied to the persistence Fisher distance (default 1.). - bandwidth_fisher (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions by PersistenceFisherDistance class (default 1.). - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth = bandwidth - self.pf_ = PersistenceFisherDistance(bandwidth=bandwidth_fisher, kernel_approx=kernel_approx) - - def fit(self, X, y=None): - """ - Fit the PersistenceFisherKernel class on a list of persistence diagrams: an instance of the PersistenceFisherDistance class is fitted on the diagrams and then stored. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.pf_.fit(X, y) - return self - - def transform(self, X): - """ - Compute all persistence Fisher kernel values between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher kernel values. - """ - return np.exp(-self.pf_.transform(X)/self.bandwidth) - diff --git a/src/python/gudhi/sktda/metrics.py b/src/python/gudhi/sktda/metrics.py deleted file mode 100644 index c512cb82..00000000 --- a/src/python/gudhi/sktda/metrics.py +++ /dev/null @@ -1,243 +0,0 @@ -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Mathieu Carrière -# -# Copyright (C) 2018-2019 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.metrics import pairwise_distances -try: - from .. import bottleneck_distance - USE_GUDHI = True -except ImportError: - USE_GUDHI = False - print("Gudhi built without CGAL: BottleneckDistance will return a null matrix") - -############################################# -# Metrics ################################### -############################################# - -class SlicedWassersteinDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the sliced Wasserstein distance matrix from a list of persistence diagrams. The Sliced Wasserstein distance is computed by projecting the persistence diagrams onto lines, comparing the projections with the 1-norm, and finally integrating over all possible lines. See http://proceedings.mlr.press/v70/carriere17a.html for more details. - """ - def __init__(self, num_directions=10): - """ - Constructor for the SlicedWassersteinDistance class. - - Parameters: - num_directions (int): number of lines evenly sampled from [-pi/2,pi/2] in order to approximate and speed up the distance computation (default 10). - """ - self.num_directions = num_directions - thetas = np.linspace(-np.pi/2, np.pi/2, num=self.num_directions+1)[np.newaxis,:-1] - self.lines_ = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=0) - - def fit(self, X, y=None): - """ - Fit the SlicedWassersteinDistance class on a list of persistence diagrams: persistence diagrams are projected onto the different lines. The diagrams themselves and their projections are then stored in numpy arrays, called **diagrams_** and **approx_diag_**. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - self.approx_ = [np.matmul(X[i], self.lines_) for i in range(len(X))] - diag_proj = (1./2) * np.ones((2,2)) - self.approx_diag_ = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] - return self - - def transform(self, X): - """ - Compute all sliced Wasserstein distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise sliced Wasserstein distances. - """ - Xfit = np.zeros((len(X), len(self.approx_))) - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - for i in range(len(self.approx_)): - for j in range(i+1, len(self.approx_)): - A = np.sort(np.concatenate([self.approx_[i], self.approx_diag_[j]], axis=0), axis=0) - B = np.sort(np.concatenate([self.approx_[j], self.approx_diag_[i]], axis=0), axis=0) - L1 = np.sum(np.abs(A-B), axis=0) - Xfit[i,j] = np.mean(L1) - Xfit[j,i] = Xfit[i,j] - else: - diag_proj = (1./2) * np.ones((2,2)) - approx = [np.matmul(X[i], self.lines_) for i in range(len(X))] - approx_diag = [np.matmul(np.matmul(X[i], diag_proj), self.lines_) for i in range(len(X))] - for i in range(len(approx)): - for j in range(len(self.approx_)): - A = np.sort(np.concatenate([approx[i], self.approx_diag_[j]], axis=0), axis=0) - B = np.sort(np.concatenate([self.approx_[j], approx_diag[i]], axis=0), axis=0) - L1 = np.sum(np.abs(A-B), axis=0) - Xfit[i,j] = np.mean(L1) - - return Xfit - -class BottleneckDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. - """ - def __init__(self, epsilon=1e-3): - """ - Constructor for the BottleneckDistance class. - - Parameters: - epsilon (double): approximation quality (default 1e-4). - """ - self.epsilon = epsilon - - def fit(self, X, y=None): - """ - Fit the BottleneckDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams**. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - return self - - def transform(self, X): - """ - Compute all bottleneck distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise bottleneck distances. - """ - num_diag1 = len(X) - - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - matrix = np.zeros((num_diag1, num_diag1)) - - if USE_GUDHI: - for i in range(num_diag1): - for j in range(i+1, num_diag1): - matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) - matrix[j,i] = matrix[i,j] - else: - print("Gudhi required---returning null matrix") - - else: - num_diag2 = len(self.diagrams_) - matrix = np.zeros((num_diag1, num_diag2)) - - if USE_GUDHI: - for i in range(num_diag1): - for j in range(num_diag2): - matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) - else: - print("Gudhi required---returning null matrix") - - Xfit = matrix - - return Xfit - -class PersistenceFisherDistance(BaseEstimator, TransformerMixin): - """ - This is a class for computing the persistence Fisher distance matrix from a list of persistence diagrams. The persistence Fisher distance is obtained by computing the original Fisher distance between the probability distributions associated to the persistence diagrams given by convolving them with a Gaussian kernel. See http://papers.nips.cc/paper/8205-persistence-fisher-kernel-a-riemannian-manifold-kernel-for-persistence-diagrams for more details. - """ - def __init__(self, bandwidth=1., kernel_approx=None): - """ - Constructor for the PersistenceFisherDistance class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel used to turn persistence diagrams into probability distributions (default 1.). - kernel_approx (class): kernel approximation class used to speed up computation (default None). Common kernel approximations classes can be found in the scikit-learn library (such as RBFSampler for instance). - """ - self.bandwidth, self.kernel_approx = bandwidth, kernel_approx - - def fit(self, X, y=None): - """ - Fit the PersistenceFisherDistance class on a list of persistence diagrams: persistence diagrams are stored in a numpy array called **diagrams** and the kernel approximation class (if not None) is applied on them. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.diagrams_ = X - projection = (1./2) * np.ones((2,2)) - self.diagonal_projections_ = [np.matmul(X[i], projection) for i in range(len(X))] - if self.kernel_approx is not None: - self.approx_ = [self.kernel_approx.transform(X[i]) for i in range(len(X))] - self.approx_diagonal_ = [self.kernel_approx.transform(self.diagonal_projections_[i]) for i in range(len(X))] - return self - - def transform(self, X): - """ - Compute all persistence Fisher distances between the persistence diagrams that were stored after calling the fit() method, and a given list of (possibly different) persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array of shape (number of diagrams in **diagrams**) x (number of diagrams in X): matrix of pairwise persistence Fisher distances. - """ - Xfit = np.zeros((len(X), len(self.diagrams_))) - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): - for i in range(len(self.diagrams_)): - for j in range(i+1, len(self.diagrams_)): - if self.kernel_approx is not None: - Z = np.concatenate([self.approx_[i], self.approx_diagonal_[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) - U, V = np.sum(np.concatenate([self.approx_[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], self.approx_diagonal_[i]], axis=0), axis=0) - vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - Xfit[j,i] = Xfit[i,j] - else: - Z = np.concatenate([self.diagrams_[i], self.diagonal_projections_[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) - U, V = np.concatenate([self.diagrams_[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], self.diagonal_projections_[i]], axis=0) - vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - Xfit[j,i] = Xfit[i,j] - else: - projection = (1./2) * np.ones((2,2)) - diagonal_projections = [np.matmul(X[i], projection) for i in range(len(X))] - if self.kernel_approx is not None: - approx = [self.kernel_approx.transform(X[i]) for i in range(len(X))] - approx_diagonal = [self.kernel_approx.transform(diagonal_projections[i]) for i in range(len(X))] - for i in range(len(X)): - for j in range(len(self.diagrams_)): - if self.kernel_approx is not None: - Z = np.concatenate([approx[i], approx_diagonal[i], self.approx_[j], self.approx_diagonal_[j]], axis=0) - U, V = np.sum(np.concatenate([approx[i], self.approx_diagonal_[j]], axis=0), axis=0), np.sum(np.concatenate([self.approx_[j], approx_diagonal[i]], axis=0), axis=0) - vectori, vectorj = np.abs(np.matmul(Z, U.T)), np.abs(np.matmul(Z, V.T)) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - else: - Z = np.concatenate([X[i], diagonal_projections[i], self.diagrams_[j], self.diagonal_projections_[j]], axis=0) - U, V = np.concatenate([X[i], self.diagonal_projections_[j]], axis=0), np.concatenate([self.diagrams_[j], diagonal_projections[i]], axis=0) - vectori = np.sum(np.exp(-np.square(pairwise_distances(Z,U))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectorj = np.sum(np.exp(-np.square(pairwise_distances(Z,V))/(2 * np.square(self.bandwidth)))/(self.bandwidth * np.sqrt(2*np.pi)), axis=1) - vectori_sum, vectorj_sum = np.sum(vectori), np.sum(vectorj) - if vectori_sum != 0: - vectori = vectori/vectori_sum - if vectorj_sum != 0: - vectorj = vectorj/vectorj_sum - Xfit[i,j] = np.arccos( min(np.dot(np.sqrt(vectori), np.sqrt(vectorj)), 1.) ) - return Xfit diff --git a/src/python/gudhi/sktda/preprocessing.py b/src/python/gudhi/sktda/preprocessing.py deleted file mode 100644 index 83227ca1..00000000 --- a/src/python/gudhi/sktda/preprocessing.py +++ /dev/null @@ -1,305 +0,0 @@ -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Mathieu Carrière -# -# Copyright (C) 2018-2019 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.preprocessing import StandardScaler - -############################################# -# Preprocessing ############################# -############################################# - -class BirthPersistenceTransform(BaseEstimator, TransformerMixin): - """ - This is a class for the affine transformation (x,y) -> (x,y-x) to be applied on persistence diagrams. - """ - def __init__(self): - """ - Constructor for BirthPersistenceTransform class. - """ - return None - - def fit(self, X, y=None): - """ - Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). - - Parameters: - X (n x 2 numpy array): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Apply the BirthPersistenceTransform function on the persistence diagrams. - - Parameters: - X (list of n x 2 numpy array): input persistence diagrams. - - Returns: - list of n x 2 numpy array: transformed persistence diagrams. - """ - Xfit = [] - for diag in X: - #new_diag = np.empty(diag.shape) - #np.copyto(new_diag, diag) - new_diag = np.copy(diag) - new_diag[:,1] = new_diag[:,1] - new_diag[:,0] - Xfit.append(new_diag) - return Xfit - -class Clamping(BaseEstimator, TransformerMixin): - """ - This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. - """ - def __init__(self, limit=np.inf): - """ - Constructor for the Clamping class. - - Parameters: - limit (double): clamping value (default np.inf). - """ - self.limit = limit - - def fit(self, X, y=None): - """ - Fit the Clamping class on a list of values (this function actually does nothing but is useful when Clamping is included in a scikit-learn Pipeline). - - Parameters: - X (numpy array of size n): input values. - y (n x 1 array): value labels (unused). - """ - return self - - def transform(self, X): - """ - Clamp list of values. - - Parameters: - X (numpy array of size n): input list of values. - - Returns: - numpy array of size n: output list of values. - """ - Xfit = np.minimum(X, self.limit) - #Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) - return Xfit - -class DiagramScaler(BaseEstimator, TransformerMixin): - """ - This is a class for preprocessing persistence diagrams with a given list of scalers, such as those included in scikit-learn. - """ - def __init__(self, use=False, scalers=[]): - """ - Constructor for the DiagramScaler class. - - Parameters: - use (bool): whether to use the class or not (default False). - scalers (list of classes): list of scalers to be fit on the persistence diagrams (default []). Each element of the list is a tuple with two elements: the first one is a list of coordinates, and the second one is a scaler (i.e. a class with fit() and transform() methods) that is going to be applied to these coordinates. Common scalers can be found in the scikit-learn library (such as MinMaxScaler for instance). - """ - self.scalers = scalers - self.use = use - - def fit(self, X, y=None): - """ - Fit the DiagramScaler class on a list of persistence diagrams: persistence diagrams are concatenated in a big numpy array, and scalers are fit (by calling their fit() method) on their corresponding coordinates in this big array. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if self.use: - if len(X) == 1: - P = X[0] - else: - P = np.concatenate(X,0) - for (indices, scaler) in self.scalers: - scaler.fit(np.reshape(P[:,indices], [-1, 1])) - return self - - def transform(self, X): - """ - Apply the DiagramScaler function on the persistence diagrams. The fitted scalers are applied (by calling their transform() method) to their corresponding coordinates in each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - list of n x 2 or n x 1 numpy arrays: transformed persistence diagrams. - """ - Xfit = [np.copy(d) for d in X] - if self.use: - for i in range(len(Xfit)): - if Xfit[i].shape[0] > 0: - for (indices, scaler) in self.scalers: - for I in indices: - Xfit[i][:,I] = np.squeeze(scaler.transform(np.reshape(Xfit[i][:,I], [-1,1]))) - return Xfit - -class Padding(BaseEstimator, TransformerMixin): - """ - This is a class for padding a list of persistence diagrams with dummy points, so that all persistence diagrams end up with the same number of points. - """ - def __init__(self, use=False): - """ - Constructor for the Padding class. - - Parameters: - use (bool): whether to use the class or not (default False). - """ - self.use = use - - def fit(self, X, y=None): - """ - Fit the Padding class on a list of persistence diagrams (this function actually does nothing but is useful when Padding is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - self.max_pts = max([len(diag) for diag in X]) - return self - - def transform(self, X): - """ - Add dummy points to each persistence diagram so that they all have the same cardinality. All points are given an additional coordinate indicating if the point was added after padding (0) or already present before (1). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - list of n x 3 or n x 2 numpy arrays: padded persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - for diag in X: - diag_pad = np.pad(diag, ((0,max(0, self.max_pts - diag.shape[0])), (0,1)), "constant", constant_values=((0,0),(0,0))) - diag_pad[:diag.shape[0],2] = np.ones(diag.shape[0]) - Xfit.append(diag_pad) - else: - Xfit = X - return Xfit - -class ProminentPoints(BaseEstimator, TransformerMixin): - """ - This is a class for removing points that are close or far from the diagonal in persistence diagrams. If persistence diagrams are n x 2 numpy arrays (i.e. persistence diagrams with ordinary features), points are ordered and thresholded by distance-to-diagonal. If persistence diagrams are n x 1 numpy arrays (i.e. persistence diagrams with essential features), points are not ordered and thresholded by first coordinate. - """ - def __init__(self, use=False, num_pts=10, threshold=-1, location="upper"): - """ - Constructor for the ProminentPoints class. - - Parameters: - use (bool): whether to use the class or not (default False). - location (string): either "upper" or "lower" (default "upper"). Whether to keep the points that are far away ("upper") or close ("lower") to the diagonal. - num_pts (int): cardinality threshold (default 10). If location == "upper", keep the top **num_pts** points that are the farthest away from the diagonal. If location == "lower", keep the top **num_pts** points that are the closest to the diagonal. - threshold (double): distance-to-diagonal threshold (default -1). If location == "upper", keep the points that are at least at a distance **threshold** from the diagonal. If location == "lower", keep the points that are at most at a distance **threshold** from the diagonal. - """ - self.num_pts = num_pts - self.threshold = threshold - self.use = use - self.location = location - - def fit(self, X, y=None): - """ - Fit the ProminentPoints class on a list of persistence diagrams (this function actually does nothing but is useful when ProminentPoints is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - If location == "upper", first select the top **num_pts** points that are the farthest away from the diagonal, then select and return from these points the ones that are at least at distance **threshold** from the diagonal for each persistence diagram individually. If location == "lower", first select the top **num_pts** points that are the closest to the diagonal, then select and return from these points the ones that are at most at distance **threshold** from the diagonal for each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - list of n x 2 or n x 1 numpy arrays: thresholded persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - for i in range(num_diag): - diag = X[i] - if diag.shape[1] >= 2: - if diag.shape[0] > 0: - pers = np.abs(diag[:,1] - diag[:,0]) - idx_thresh = pers >= self.threshold - thresh_diag, thresh_pers = diag[idx_thresh], pers[idx_thresh] - sort_index = np.flip(np.argsort(thresh_pers, axis=None), 0) - if self.location == "upper": - new_diag = thresh_diag[sort_index[:min(self.num_pts, thresh_diag.shape[0])],:] - if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[sort_index[min(self.num_pts, thresh_diag.shape[0]):],:], diag[~idx_thresh] ], axis=0) - else: - new_diag = diag - - else: - if diag.shape[0] > 0: - birth = diag[:,:1] - idx_thresh = birth >= self.threshold - thresh_diag, thresh_birth = diag[idx_thresh], birth[idx_thresh] - if self.location == "upper": - new_diag = thresh_diag[:min(self.num_pts, thresh_diag.shape[0]),:] - if self.location == "lower": - new_diag = np.concatenate( [ thresh_diag[min(self.num_pts, thresh_diag.shape[0]):,:], diag[~idx_thresh] ], axis=0) - else: - new_diag = diag - - Xfit.append(new_diag) - else: - Xfit = X - return Xfit - -class DiagramSelector(BaseEstimator, TransformerMixin): - """ - This is a class for extracting finite or essential points in persistence diagrams. - """ - def __init__(self, use=False, limit=np.inf, point_type="finite"): - """ - Constructor for the DiagramSelector class. - - Parameters: - use (bool): whether to use the class or not (default False). - limit (double): second coordinate value that is the criterion for being an essential point (default numpy.inf). - point_type (string): either "finite" or "essential". The type of the points that are going to be extracted. - """ - self.use, self.limit, self.point_type = use, limit, point_type - - def fit(self, X, y=None): - """ - Fit the DiagramSelector class on a list of persistence diagrams (this function actually does nothing but is useful when DiagramSelector is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Extract and return the finite or essential points of each persistence diagram individually. - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - - Returns: - list of n x 2 or n x 1 numpy arrays: extracted persistence diagrams. - """ - if self.use: - Xfit, num_diag = [], len(X) - if self.point_type == "finite": - Xfit = [ diag[diag[:,1] < self.limit] if diag.shape[0] != 0 else diag for diag in X] - else: - Xfit = [ diag[diag[:,1] >= self.limit, 0:1] if diag.shape[0] != 0 else diag for diag in X] - else: - Xfit = X - return Xfit diff --git a/src/python/gudhi/sktda/vector_methods.py b/src/python/gudhi/sktda/vector_methods.py deleted file mode 100644 index bf32f18e..00000000 --- a/src/python/gudhi/sktda/vector_methods.py +++ /dev/null @@ -1,485 +0,0 @@ -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Mathieu Carrière -# -# Copyright (C) 2018-2019 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - -import numpy as np -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.preprocessing import MinMaxScaler, MaxAbsScaler -from sklearn.neighbors import DistanceMetric - -from .preprocessing import DiagramScaler, BirthPersistenceTransform - -############################################# -# Finite Vectorization methods ############## -############################################# - -class PersistenceImage(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence images from a list of persistence diagrams. A persistence image is a 2D function computed from a persistence diagram by convolving the diagram points with a weighted Gaussian kernel. The plane is then discretized into an image with pixels, which is flattened and returned as a vector. See http://jmlr.org/papers/v18/16-337.html for more details. - """ - def __init__(self, bandwidth=1., weight=lambda x: 1, resolution=[20,20], im_range=[np.nan, np.nan, np.nan, np.nan]): - """ - Constructor for the PersistenceImage class. - - Parameters: - bandwidth (double): bandwidth of the Gaussian kernel (default 1.). - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie lists or numpy arrays of the form [p_x,p_y]. - resolution ([int,int]): size (in pixels) of the persistence image (default [20,20]). - im_range ([double,double,double,double]): minimum and maximum of each axis of the persistence image, of the form [x_min, x_max, y_min, y_max] (default [numpy.nan, numpy.nan, numpy.nan, numpyp.nan]). If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.bandwidth, self.weight = bandwidth, weight - self.resolution, self.im_range = resolution, im_range - - def fit(self, X, y=None): - """ - Fit the PersistenceImage class on a list of persistence diagrams: if any of the values in **im_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.im_range)).any(): - new_X = BirthPersistenceTransform().fit_transform(X) - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(new_X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.im_range = np.where(np.isnan(np.array(self.im_range)), np.array([mx, Mx, my, My]), np.array(self.im_range)) - return self - - def transform(self, X): - """ - Compute the persistence image for each persistence diagram individually and store the results in a single numpy array. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (number of pixels = **resolution[0]** x **resolution[1]**): output persistence images. - """ - num_diag, Xfit = len(X), [] - new_X = BirthPersistenceTransform().fit_transform(X) - - for i in range(num_diag): - - diagram, num_pts_in_diag = new_X[i], X[i].shape[0] - - w = np.empty(num_pts_in_diag) - for j in range(num_pts_in_diag): - w[j] = self.weight(diagram[j,:]) - - x_values, y_values = np.linspace(self.im_range[0], self.im_range[1], self.resolution[0]), np.linspace(self.im_range[2], self.im_range[3], self.resolution[1]) - Xs, Ys = np.tile((diagram[:,0][:,np.newaxis,np.newaxis]-x_values[np.newaxis,np.newaxis,:]),[1,self.resolution[1],1]), np.tile(diagram[:,1][:,np.newaxis,np.newaxis]-y_values[np.newaxis,:,np.newaxis],[1,1,self.resolution[0]]) - image = np.tensordot(w, np.exp((-np.square(Xs)-np.square(Ys))/(2*np.square(self.bandwidth)))/(np.square(self.bandwidth)*2*np.pi), 1) - - Xfit.append(image.flatten()[np.newaxis,:]) - - Xfit = np.concatenate(Xfit,0) - - return Xfit - -class Landscape(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. - """ - def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Landscape class. - - Parameters: - num_landscapes (int): number of piecewise-linear functions to output (default 5). - resolution (int): number of sample for all piecewise-linear functions (default 100). - sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Landscape class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the persistence landscape for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**): output persistence landscapes. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - ls = np.zeros([self.num_landscapes, self.resolution]) - - events = [] - for j in range(self.resolution): - events.append([]) - - for j in range(num_pts_in_diag): - [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - - if min_idx < self.resolution and max_idx > 0: - - landscape_value = self.sample_range[0] + min_idx * step_x - px - for k in range(min_idx, mid_idx): - events[k].append(landscape_value) - landscape_value += step_x - - landscape_value = py - self.sample_range[0] - mid_idx * step_x - for k in range(mid_idx, max_idx): - events[k].append(landscape_value) - landscape_value -= step_x - - for j in range(self.resolution): - events[j].sort(reverse=True) - for k in range( min(self.num_landscapes, len(events[j])) ): - ls[k,j] = events[j][k] - - Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) - - Xfit = np.concatenate(Xfit,0) - - return Xfit - -class Silhouette(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. - """ - def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Silhouette class. - - Parameters: - weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. - resolution (int): number of samples for the weighted average (default 100). - sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.weight, self.resolution, self.sample_range = weight, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Silhouette class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the persistence silhouette for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (**resolution**): output persistence silhouettes. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - sh, weights = np.zeros(self.resolution), np.zeros(num_pts_in_diag) - for j in range(num_pts_in_diag): - weights[j] = self.weight(diagram[j,:]) - total_weight = np.sum(weights) - - for j in range(num_pts_in_diag): - - [px,py] = diagram[j,:2] - weight = weights[j] / total_weight - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - - if min_idx < self.resolution and max_idx > 0: - - silhouette_value = self.sample_range[0] + min_idx * step_x - px - for k in range(min_idx, mid_idx): - sh[k] += weight * silhouette_value - silhouette_value += step_x - - silhouette_value = py - self.sample_range[0] - mid_idx * step_x - for k in range(mid_idx, max_idx): - sh[k] += weight * silhouette_value - silhouette_value -= step_x - - Xfit.append(np.reshape(np.sqrt(2) * sh, [1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class BettiCurve(BaseEstimator, TransformerMixin): - """ - This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. - """ - def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the BettiCurve class. - - Parameters: - resolution (int): number of sample for the piecewise-constant function (default 100). - sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. - """ - self.resolution, self.sample_range = resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the BettiCurve class on a list of persistence diagrams: if any of the values in **sample_range** is numpy.nan, replace it with the corresponding value computed on the given list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the Betti curve for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (**resolution**): output Betti curves. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - - bc = np.zeros(self.resolution) - for j in range(num_pts_in_diag): - [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - for k in range(min_idx, max_idx): - bc[k] += 1 - - Xfit.append(np.reshape(bc,[1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class Entropy(BaseEstimator, TransformerMixin): - """ - This is a class for computing persistence entropy. Persistence entropy is a statistic for persistence diagrams inspired from Shannon entropy. This statistic can also be used to compute a feature vector, called the entropy summary function. See https://arxiv.org/pdf/1803.08304.pdf for more details. Note that a previous implementation was contributed by Manuel Soriano-Trigueros. - """ - def __init__(self, mode="scalar", normalized=True, resolution=100, sample_range=[np.nan, np.nan]): - """ - Constructor for the Entropy class. - - Parameters: - mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). - normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". - resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". - sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". - """ - self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range - - def fit(self, X, y=None): - """ - Fit the Entropy class on a list of persistence diagrams. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - if np.isnan(np.array(self.sample_range)).any(): - pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) - [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) - return self - - def transform(self, X): - """ - Compute the entropy for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (1 if **mode** = "scalar" else **resolution**): output entropy. - """ - num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) - step_x = x_values[1] - x_values[0] - new_X = BirthPersistenceTransform().fit_transform(X) - - for i in range(num_diag): - - orig_diagram, diagram, num_pts_in_diag = X[i], new_X[i], X[i].shape[0] - new_diagram = DiagramScaler(use=True, scalers=[([1], MaxAbsScaler())]).fit_transform([diagram])[0] - - if self.mode == "scalar": - ent = - np.sum( np.multiply(new_diagram[:,1], np.log(new_diagram[:,1])) ) - Xfit.append(np.array([[ent]])) - - else: - ent = np.zeros(self.resolution) - for j in range(num_pts_in_diag): - [px,py] = orig_diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - for k in range(min_idx, max_idx): - ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) - if self.normalized: - ent = ent / np.linalg.norm(ent, ord=1) - Xfit.append(np.reshape(ent,[1,-1])) - - Xfit = np.concatenate(Xfit, 0) - - return Xfit - -class TopologicalVector(BaseEstimator, TransformerMixin): - """ - This is a class for computing topological vectors from a list of persistence diagrams. The topological vector associated to a persistence diagram is the sorted vector of a slight modification of the pairwise distances between the persistence diagram points. See https://diglib.eg.org/handle/10.1111/cgf12692 for more details. - """ - def __init__(self, threshold=10): - """ - Constructor for the TopologicalVector class. - - Parameters: - threshold (int): number of distances to keep (default 10). This is the dimension of the topological vector. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding topological vector as threshold. - """ - self.threshold = threshold - - def fit(self, X, y=None): - """ - Fit the TopologicalVector class on a list of persistence diagrams (this function actually does nothing but is useful when TopologicalVector is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Compute the topological vector for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (**threshold**): output topological vectors. - """ - if self.threshold == -1: - thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() - else: - thresh = self.threshold - - num_diag = len(X) - Xfit = np.zeros([num_diag, thresh]) - - for i in range(num_diag): - - diagram, num_pts_in_diag = X[i], X[i].shape[0] - pers = 0.5 * (diagram[:,1]-diagram[:,0]) - min_pers = np.minimum(pers,np.transpose(pers)) - distances = DistanceMetric.get_metric("chebyshev").pairwise(diagram) - vect = np.flip(np.sort(np.triu(np.minimum(distances, min_pers)), axis=None), 0) - dim = min(len(vect), thresh) - Xfit[i, :dim] = vect[:dim] - - return Xfit - -class ComplexPolynomial(BaseEstimator, TransformerMixin): - """ - This is a class for computing complex polynomials from a list of persistence diagrams. The persistence diagram points are seen as the roots of some complex polynomial, whose coefficients are returned in a complex vector. See https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27 for more details. - """ - def __init__(self, polynomial_type="R", threshold=10): - """ - Constructor for the ComplexPolynomial class. - - Parameters: - polynomial_type (char): either "R", "S" or "T" (default "R"). Type of complex polynomial that is going to be computed (explained in https://link.springer.com/chapter/10.1007%2F978-3-319-23231-7_27). - threshold (int): number of coefficients (default 10). This is the dimension of the complex vector of coefficients, i.e. the number of coefficients corresponding to the largest degree terms of the polynomial. If -1, this threshold is computed from the list of persistence diagrams by considering the one with the largest number of points and using the dimension of its corresponding complex vector of coefficients as threshold. - """ - self.threshold, self.polynomial_type = threshold, polynomial_type - - def fit(self, X, y=None): - """ - Fit the ComplexPolynomial class on a list of persistence diagrams (this function actually does nothing but is useful when ComplexPolynomial is included in a scikit-learn Pipeline). - - Parameters: - X (list of n x 2 or n x 1 numpy arrays): input persistence diagrams. - y (n x 1 array): persistence diagram labels (unused). - """ - return self - - def transform(self, X): - """ - Compute the complex vector of coefficients for each persistence diagram individually and concatenate the results. - - Parameters: - X (list of n x 2 numpy arrays): input persistence diagrams. - - Returns: - numpy array with shape (number of diagrams) x (**threshold**): output complex vectors of coefficients. - """ - if self.threshold == -1: - thresh = np.array([X[i].shape[0] for i in range(len(X))]).max() - else: - thresh = self.threshold - - Xfit = np.zeros([len(X), thresh]) + 1j * np.zeros([len(X), thresh]) - for d in range(len(X)): - D, N = X[d], X[d].shape[0] - if self.polynomial_type == "R": - roots = D[:,0] + 1j * D[:,1] - elif self.polynomial_type == "S": - alpha = np.linalg.norm(D, axis=1) - alpha = np.where(alpha==0, np.ones(N), alpha) - roots = np.multiply( np.multiply( (D[:,0]+1j*D[:,1]), (D[:,1]-D[:,0]) ), 1./(np.sqrt(2)*alpha) ) - elif self.polynomial_type == "T": - alpha = np.linalg.norm(D, axis=1) - roots = np.multiply( (D[:,1]-D[:,0])/2, np.cos(alpha) - np.sin(alpha) + 1j * (np.cos(alpha) + np.sin(alpha)) ) - coeff = [0] * (N+1) - coeff[N] = 1 - for i in range(1, N+1): - for j in range(N-i-1, N): - coeff[j] += ((-1) * roots[i-1] * coeff[j+1]) - coeff = np.array(coeff[::-1])[1:] - Xfit[d, :min(thresh, coeff.shape[0])] = coeff[:min(thresh, coeff.shape[0])] - return Xfit -- cgit v1.2.3 From 57b86b2665cd0e35d18b697577b00c604212e369 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 15 Nov 2019 23:09:29 +0100 Subject: Token documentation --- src/python/doc/representations.rst | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index 3db1c95a..a137a035 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -2,9 +2,22 @@ .. To get rid of WARNING: document isn't included in any toctree -=================================== -Representations reference manual -=================================== +====================== +Representations manual +====================== + +.. include:: representations_sum.inc + +This module, originally named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning tools, in particular scikit-learn. It provides tools, using the scikit-learn standard interface, to compute distances and kernels on diagrams, and to convert diagrams into vectors. + +A diagram is represented as a numpy array of shape (n,2), as can be obtained from `SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. + +A small example is provided + +.. only:: builder_html + + * :download:`diagram_vectorizations_distances_kernels.py <../example/diagram_vectorizations_distances_kernels.py>` + Preprocessing ------------- -- cgit v1.2.3 From 4eb1d8dd6a2da695d989c43679161f57dd940a70 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 15 Nov 2019 23:25:24 +0100 Subject: Example uses matplotlib --- src/python/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 2c568b66..9af85eac 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -388,9 +388,9 @@ if(PYTHONINTERP_FOUND) endif(OT_FOUND) # Representations - if(SKLEARN_FOUND) + if(SKLEARN_FOUND AND MATPLOTLIB_FOUND) add_gudhi_py_test(test_representations) - endif(SKLEARN_FOUND) + endif() # Documentation generation is available through sphinx - requires all modules if(SPHINX_PATH) -- cgit v1.2.3 From 6d2e5d1cbdecd86ad4d56fc8902542caca2cf2bd Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 15 Nov 2019 23:33:49 +0100 Subject: Code review: Point_3 is an Alpha_shape_3::Point instead of std::conditionnal(...) --- src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index 70e864e6..7f96c94c 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -252,7 +252,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_ /** \brief `Alpha_complex_3d::Point_3` type is either a `Alpha_complex_3d::Bare_point_3` (Weighted = false) or a * `Alpha_complex_3d::Weighted_point_3` (Weighted = true). */ - using Point_3 = typename std::conditional::type; + using Point_3 = typename Alpha_shape_3::Point; private: using Dispatch = -- cgit v1.2.3 From c3414741af6ef98703ba9414803aff5a2d08777d Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 06:34:54 +0100 Subject: More 'is' to '==' --- src/python/gudhi/alpha_complex.pyx | 2 +- src/python/gudhi/cubical_complex.pyx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index 6d6309db..a85c47c8 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -68,7 +68,7 @@ cdef class AlphaComplex: # The real cython constructor def __cinit__(self, points=None, off_file=''): - if off_file is not '': + if off_file: if os.path.isfile(off_file): self.thisptr = new Alpha_complex_interface(str.encode(off_file), True) else: diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 0dc133d1..579c942b 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -66,9 +66,9 @@ cdef class CubicalComplex: # The real cython constructor def __cinit__(self, dimensions=None, top_dimensional_cells=None, perseus_file=''): - if (dimensions is not None) and (top_dimensional_cells is not None) and (perseus_file is ''): + if (dimensions is not None) and (top_dimensional_cells is not None) and (perseus_file == ''): self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) - elif (dimensions is None) and (top_dimensional_cells is None) and (perseus_file is not ''): + elif (dimensions is None) and (top_dimensional_cells is None) and (perseus_file != ''): if os.path.isfile(perseus_file): self.thisptr = new Bitmap_cubical_complex_base_interface(str.encode(perseus_file)) else: -- cgit v1.2.3 From 00985c119ae164477e8493a69f32f103774cf51c Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 14:05:19 +0100 Subject: Examples for plotting a simplicial complex with plotly / matplotlib --- src/python/example/plot_alpha_complex.py | 34 ++++++++++++++++++++++++++++++++ src/python/example/plot_rips_complex.py | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100755 src/python/example/plot_alpha_complex.py create mode 100755 src/python/example/plot_rips_complex.py diff --git a/src/python/example/plot_alpha_complex.py b/src/python/example/plot_alpha_complex.py new file mode 100755 index 00000000..98931975 --- /dev/null +++ b/src/python/example/plot_alpha_complex.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import numpy as np +import gudhi +ac = gudhi.AlphaComplex(off_file='../../data/points/tore3D_1307.off') +st = ac.create_simplex_tree() +points = np.array([ac.get_point(i) for i in range(st.num_vertices())]) +# We want to plot the alpha-complex with alpha=0.1. +# We are only going to plot the triangles +triangles = np.array([s[0] for s in st.get_skeleton(2) if len(s[0])==3 and s[1] <= .1]) + +# First possibility: plotly +import plotly.graph_objects as go +fig = go.Figure(data=[ + go.Mesh3d( + x=points[:,0], + y=points[:,1], + z=points[:,2], + i = triangles[:,0], + j = triangles[:,1], + k = triangles[:,2], + ) +]) +fig.show() + +# Second possibility: matplotlib +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt +fig = plt.figure() +ax = fig.gca(projection='3d') +ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) +plt.show() + +# Third possibility: mayavi.mlab.triangular_mesh diff --git a/src/python/example/plot_rips_complex.py b/src/python/example/plot_rips_complex.py new file mode 100755 index 00000000..d2637ea8 --- /dev/null +++ b/src/python/example/plot_rips_complex.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import numpy as np +import gudhi +points = np.array(gudhi.read_off('../../data/points/Kl.off')) +rc = gudhi.RipsComplex(points=points, max_edge_length=.2) +st = rc.create_simplex_tree(max_dimension=2) +# We are only going to plot the triangles +triangles = np.array([s[0] for s in st.get_skeleton(2) if len(s[0])==3]) + +# First possibility: plotly +import plotly.graph_objects as go +fig = go.Figure(data=[ + go.Mesh3d( + # Use the first 3 coordinates, but we could as easily pick others + x=points[:,0], + y=points[:,1], + z=points[:,2], + i = triangles[:,0], + j = triangles[:,1], + k = triangles[:,2], + ) +]) +fig.show() + +# Second possibility: matplotlib +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt +fig = plt.figure() +ax = fig.gca(projection='3d') +ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) +plt.show() + +# Third possibility: mayavi.mlab.triangular_mesh -- cgit v1.2.3 From 71ffd4fcbf273289a556ee5ed39a6b8edf8444a1 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 14:27:52 +0100 Subject: Add mayavi to the examples --- src/python/example/plot_alpha_complex.py | 5 ++++- src/python/example/plot_rips_complex.py | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/python/example/plot_alpha_complex.py b/src/python/example/plot_alpha_complex.py index 98931975..99c18a7c 100755 --- a/src/python/example/plot_alpha_complex.py +++ b/src/python/example/plot_alpha_complex.py @@ -31,4 +31,7 @@ ax = fig.gca(projection='3d') ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) plt.show() -# Third possibility: mayavi.mlab.triangular_mesh +# Third possibility: mayavi +from mayavi import mlab +mlab.triangular_mesh(points[:,0], points[:,1], points[:,2], triangles); +mlab.show() diff --git a/src/python/example/plot_rips_complex.py b/src/python/example/plot_rips_complex.py index d2637ea8..1c878db1 100755 --- a/src/python/example/plot_rips_complex.py +++ b/src/python/example/plot_rips_complex.py @@ -31,4 +31,8 @@ ax = fig.gca(projection='3d') ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) plt.show() -# Third possibility: mayavi.mlab.triangular_mesh +# Third possibility: mayavi +# (this may take a while) +from mayavi import mlab +mlab.triangular_mesh(points[:,0], points[:,1], points[:,2], triangles); +mlab.show() -- cgit v1.2.3 From 7880c08d7ff7c42d7b089f9d1252c85289bf7596 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 15:11:06 +0100 Subject: Example plot of triangles, edges and vertices with mayavi --- src/python/example/plot_simplex_tree_dim012.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 src/python/example/plot_simplex_tree_dim012.py diff --git a/src/python/example/plot_simplex_tree_dim012.py b/src/python/example/plot_simplex_tree_dim012.py new file mode 100755 index 00000000..e6a1a4e0 --- /dev/null +++ b/src/python/example/plot_simplex_tree_dim012.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +import numpy as np +import gudhi + +# Coordinates of the points +points=np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1],[1,1,1],[1,1,0],[0,1,1]]) +# Build the simplicial complex with a tetrahedon, an edge and an isolated vertex +cplx=gudhi.SimplexTree() +cplx.insert([1,2,3,5]) +cplx.insert([4,6]) +cplx.insert([0]) + +from mayavi import mlab +# Plot triangles +triangles = np.array([s[0] for s in cplx.get_skeleton(2) if len(s[0])==3]) +mlab.triangular_mesh(points[:,0], points[:,1], points[:,2], triangles); +# Plot edges +for s in cplx.get_skeleton(1): + e = s[0] + if len(e) == 2: + pts = points[[e[0],e[1]]] + mlab.plot3d(pts[:,0],pts[:,1],pts[:,2],tube_radius=None) +# Plot points +mlab.points3d(points[:,0], points[:,1], points[:,2]) +mlab.show() -- cgit v1.2.3 From eae8673750c74d1f1023862703e350b26cf35912 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 15:33:38 +0100 Subject: Add examples to the list of examples --- src/python/doc/examples.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/python/doc/examples.rst b/src/python/doc/examples.rst index edbc2f72..a42227e3 100644 --- a/src/python/doc/examples.rst +++ b/src/python/doc/examples.rst @@ -16,6 +16,9 @@ Examples * :download:`periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py <../example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py>` * :download:`bottleneck_basic_example.py <../example/bottleneck_basic_example.py>` * :download:`gudhi_graphical_tools_example.py <../example/gudhi_graphical_tools_example.py>` + * :download:`plot_simplex_tree_dim012.py <../example/plot_simplex_tree_dim012.py>` + * :download:`plot_rips_complex.py <../example/plot_rips_complex.py>` + * :download:`plot_alpha_complex.py <../example/plot_alpha_complex.py>` * :download:`witness_complex_from_nearest_landmark_table.py <../example/witness_complex_from_nearest_landmark_table.py>` * :download:`euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py>` * :download:`euclidean_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py>` -- cgit v1.2.3 From 4ed9330dc0da5963124cac1b7e7caebc0eed73cd Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 17:50:08 +0100 Subject: Draw vertex/edge/triangle with matplotlib --- src/python/example/plot_simplex_tree_dim012.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/python/example/plot_simplex_tree_dim012.py b/src/python/example/plot_simplex_tree_dim012.py index e6a1a4e0..f79c359b 100755 --- a/src/python/example/plot_simplex_tree_dim012.py +++ b/src/python/example/plot_simplex_tree_dim012.py @@ -9,17 +9,37 @@ cplx=gudhi.SimplexTree() cplx.insert([1,2,3,5]) cplx.insert([4,6]) cplx.insert([0]) +triangles = np.array([s[0] for s in cplx.get_skeleton(2) if len(s[0])==3]) +## With matplotlib +from mpl_toolkits.mplot3d import Axes3D +from mpl_toolkits.mplot3d.art3d import Line3DCollection +import matplotlib.pyplot as plt +fig = plt.figure() +ax = fig.gca(projection='3d') +# Plot triangles +ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) +# Plot points +ax.scatter3D(points[:,0], points[:,1], points[:,2]) +# Plot edges +edges=[] +for s in cplx.get_skeleton(1): + e = s[0] + if len(e) == 2: + edges.append(points[[e[0],e[1]]]) +ax.add_collection3d(Line3DCollection(segments=edges)) +plt.show() + +## With mayavi from mayavi import mlab # Plot triangles -triangles = np.array([s[0] for s in cplx.get_skeleton(2) if len(s[0])==3]) mlab.triangular_mesh(points[:,0], points[:,1], points[:,2], triangles); +# Plot points +mlab.points3d(points[:,0], points[:,1], points[:,2]) # Plot edges for s in cplx.get_skeleton(1): e = s[0] if len(e) == 2: pts = points[[e[0],e[1]]] mlab.plot3d(pts[:,0],pts[:,1],pts[:,2],tube_radius=None) -# Plot points -mlab.points3d(points[:,0], points[:,1], points[:,2]) mlab.show() -- cgit v1.2.3 From fc4f80aa2ce60fd563bb54a0d082c83fe99fd99d Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 16 Nov 2019 18:35:44 +0100 Subject: Draw vertex/edge/triangle with plotly --- src/python/example/plot_simplex_tree_dim012.py | 41 +++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/python/example/plot_simplex_tree_dim012.py b/src/python/example/plot_simplex_tree_dim012.py index f79c359b..5b962131 100755 --- a/src/python/example/plot_simplex_tree_dim012.py +++ b/src/python/example/plot_simplex_tree_dim012.py @@ -9,7 +9,36 @@ cplx=gudhi.SimplexTree() cplx.insert([1,2,3,5]) cplx.insert([4,6]) cplx.insert([0]) +# List of triangles (point indices) triangles = np.array([s[0] for s in cplx.get_skeleton(2) if len(s[0])==3]) +# List of edges (point coordinates) +edges = [] +for s in cplx.get_skeleton(1): + e = s[0] + if len(e) == 2: + edges.append(points[[e[0],e[1]]]) + +## With plotly +import plotly.graph_objects as go +# Plot triangles +f2 = go.Mesh3d( + x=points[:,0], + y=points[:,1], + z=points[:,2], + i = triangles[:,0], + j = triangles[:,1], + k = triangles[:,2], + ) +# Plot points +f0 = go.Scatter3d(x=points[:,0], y=points[:,1], z=points[:,2], mode="markers") +data = [f2, f0] +# Plot edges +for pts in edges: + seg = go.Scatter3d(x=pts[:,0],y=pts[:,1],z=pts[:,2],mode="lines",line=dict(color='green')) + data.append(seg) +fig = go.Figure(data=data,layout=dict(showlegend=False)) +# By default plotly would give each edge its own color and legend, that's too much +fig.show() ## With matplotlib from mpl_toolkits.mplot3d import Axes3D @@ -22,11 +51,6 @@ ax.plot_trisurf(points[:,0], points[:,1], points[:,2], triangles=triangles) # Plot points ax.scatter3D(points[:,0], points[:,1], points[:,2]) # Plot edges -edges=[] -for s in cplx.get_skeleton(1): - e = s[0] - if len(e) == 2: - edges.append(points[[e[0],e[1]]]) ax.add_collection3d(Line3DCollection(segments=edges)) plt.show() @@ -37,9 +61,6 @@ mlab.triangular_mesh(points[:,0], points[:,1], points[:,2], triangles); # Plot points mlab.points3d(points[:,0], points[:,1], points[:,2]) # Plot edges -for s in cplx.get_skeleton(1): - e = s[0] - if len(e) == 2: - pts = points[[e[0],e[1]]] - mlab.plot3d(pts[:,0],pts[:,1],pts[:,2],tube_radius=None) +for pts in edges: + mlab.plot3d(pts[:,0],pts[:,1],pts[:,2],tube_radius=None) mlab.show() -- cgit v1.2.3 From 669039c52bb3e71ad24f95e8a7cf1b2be69a5548 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Sat, 16 Nov 2019 23:46:34 +0100 Subject: Doc review: Add the starting point of the documentation --- src/Alpha_complex/doc/Intro_alpha_complex.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Alpha_complex/doc/Intro_alpha_complex.h b/src/Alpha_complex/doc/Intro_alpha_complex.h index fd6f4081..adc1378f 100644 --- a/src/Alpha_complex/doc/Intro_alpha_complex.h +++ b/src/Alpha_complex/doc/Intro_alpha_complex.h @@ -51,6 +51,16 @@ namespace alpha_complex { * - For people only interested in the topology of the \ref alpha_complex (for instance persistence), * \ref alpha_complex is equivalent to the \ref cech_complex and much smaller if you do not bound the radii. * \ref cech_complex can still make sense in higher dimension precisely because you can bound the radii. + * - Using the default `CGAL::Epeck_d` makes the construction safe. If you pass exact=true to create_complex, the + * filtration values are the exact ones converted to the filtration value type of the simplicial complex. This can be + * very slow. If you pass exact=false (the default), the filtration values are only guaranteed to have a small + * multiplicative error compared to the exact value, see + * CGAL::Lazy_exact_nt::set_relative_precision_of_to_double for details. A drawback, when computing + * persistence, is that an empty exact interval [10^12,10^12] may become a non-empty approximate interval + * [10^12,10^12+10^6]. Using `CGAL::Epick_d` makes the computations slightly faster, and the combinatorics are still + * exact, but the computation of filtration values can exceptionally be arbitrarily bad. In all cases, we still + * guarantee that the output is a valid filtration (faces have a filtration value no larger than their cofaces). * - For performances reasons, it is advised to use `Alpha_complex` with \ref cgal ≥ 5.0.0. * * \section pointsexample Example from points -- cgit v1.2.3 From 63616eba3031334d8e2c267e54f9aa4a801baeff Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 14 Nov 2019 13:13:38 +0100 Subject: Make the license a comment instead of a string I think this interacts better with sphinx automodule --- src/python/gudhi/__init__.py.in | 17 ++++++++--------- src/python/gudhi/alpha_complex.pyx | 17 ++++++++--------- src/python/gudhi/bottleneck.pyx | 17 ++++++++--------- src/python/gudhi/cubical_complex.pyx | 17 ++++++++--------- src/python/gudhi/euclidean_strong_witness_complex.pyx | 17 ++++++++--------- src/python/gudhi/euclidean_witness_complex.pyx | 17 ++++++++--------- src/python/gudhi/nerve_gic.pyx | 17 ++++++++--------- src/python/gudhi/off_reader.pyx | 17 ++++++++--------- src/python/gudhi/periodic_cubical_complex.pyx | 17 ++++++++--------- src/python/gudhi/persistence_graphical_tools.py | 17 ++++++++--------- src/python/gudhi/reader_utils.pyx | 17 ++++++++--------- src/python/gudhi/rips_complex.pyx | 17 ++++++++--------- src/python/gudhi/simplex_tree.pyx | 17 ++++++++--------- src/python/gudhi/strong_witness_complex.pyx | 17 ++++++++--------- src/python/gudhi/subsampling.pyx | 17 ++++++++--------- src/python/gudhi/tangential_complex.pyx | 17 ++++++++--------- src/python/gudhi/wasserstein.py | 17 ++++++++--------- src/python/gudhi/witness_complex.pyx | 17 ++++++++--------- 18 files changed, 144 insertions(+), 162 deletions(-) diff --git a/src/python/gudhi/__init__.py.in b/src/python/gudhi/__init__.py.in index 02888fff..0c462b02 100644 --- a/src/python/gudhi/__init__.py.in +++ b/src/python/gudhi/__init__.py.in @@ -1,14 +1,13 @@ from importlib import import_module -"""This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "GUDHI Editorial Board" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index a85c47c8..8f2c98d5 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -9,15 +9,14 @@ import os from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/bottleneck.pyx b/src/python/gudhi/bottleneck.pyx index 4b378cbc..c2361024 100644 --- a/src/python/gudhi/bottleneck.pyx +++ b/src/python/gudhi/bottleneck.pyx @@ -3,15 +3,14 @@ from libcpp.vector cimport vector from libcpp.utility cimport pair import os -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 579c942b..011c407c 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -7,15 +7,14 @@ import os from numpy import array as np_array -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/euclidean_strong_witness_complex.pyx b/src/python/gudhi/euclidean_strong_witness_complex.pyx index 0e5b544a..e3f451f0 100644 --- a/src/python/gudhi/euclidean_strong_witness_complex.pyx +++ b/src/python/gudhi/euclidean_strong_witness_complex.pyx @@ -6,15 +6,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/euclidean_witness_complex.pyx b/src/python/gudhi/euclidean_witness_complex.pyx index 86d2d996..84a8ea1a 100644 --- a/src/python/gudhi/euclidean_witness_complex.pyx +++ b/src/python/gudhi/euclidean_witness_complex.pyx @@ -6,15 +6,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/nerve_gic.pyx b/src/python/gudhi/nerve_gic.pyx index 2b230b8c..acb78564 100644 --- a/src/python/gudhi/nerve_gic.pyx +++ b/src/python/gudhi/nerve_gic.pyx @@ -9,15 +9,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2018 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2018 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2018 Inria" diff --git a/src/python/gudhi/off_reader.pyx b/src/python/gudhi/off_reader.pyx index 27a89870..225e981c 100644 --- a/src/python/gudhi/off_reader.pyx +++ b/src/python/gudhi/off_reader.pyx @@ -3,15 +3,14 @@ from libcpp.vector cimport vector from libcpp.string cimport string import os -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index 88223afb..c89055db 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -7,15 +7,14 @@ import os from numpy import array as np_array -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/persistence_graphical_tools.py b/src/python/gudhi/persistence_graphical_tools.py index 181bc8ea..43a00459 100644 --- a/src/python/gudhi/persistence_graphical_tools.py +++ b/src/python/gudhi/persistence_graphical_tools.py @@ -5,15 +5,14 @@ import numpy as np from gudhi.reader_utils import read_persistence_intervals_in_dimension from gudhi.reader_utils import read_persistence_intervals_grouped_by_dimension -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau, Bertrand Michel - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau, Bertrand Michel +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau, Bertrand Michel" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/reader_utils.pyx b/src/python/gudhi/reader_utils.pyx index b266c783..6994c4f9 100644 --- a/src/python/gudhi/reader_utils.pyx +++ b/src/python/gudhi/reader_utils.pyx @@ -7,15 +7,14 @@ from libcpp.pair cimport pair from os import path from numpy import array as np_array -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2017 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2017 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2017 Inria" diff --git a/src/python/gudhi/rips_complex.pyx b/src/python/gudhi/rips_complex.pyx index f2cd6a8d..cbbbab0d 100644 --- a/src/python/gudhi/rips_complex.pyx +++ b/src/python/gudhi/rips_complex.pyx @@ -8,15 +8,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 90ddf6dd..ade3bf6c 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -2,15 +2,14 @@ from libc.stdint cimport intptr_t from numpy import array as np_array cimport simplex_tree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/strong_witness_complex.pyx b/src/python/gudhi/strong_witness_complex.pyx index e35ba6ad..66d49b49 100644 --- a/src/python/gudhi/strong_witness_complex.pyx +++ b/src/python/gudhi/strong_witness_complex.pyx @@ -6,15 +6,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/subsampling.pyx b/src/python/gudhi/subsampling.pyx index e622ac99..e0cd1348 100644 --- a/src/python/gudhi/subsampling.pyx +++ b/src/python/gudhi/subsampling.pyx @@ -4,15 +4,14 @@ from libcpp.string cimport string from libcpp cimport bool import os -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/tangential_complex.pyx b/src/python/gudhi/tangential_complex.pyx index 9ef15567..b7678f4d 100644 --- a/src/python/gudhi/tangential_complex.pyx +++ b/src/python/gudhi/tangential_complex.pyx @@ -9,15 +9,14 @@ import os from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index eba7c6d5..d8a3104c 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -5,15 +5,14 @@ try: except ImportError: print("POT (Python Optimal Transport) package is not installed. Try to run $ conda install -c conda-forge pot ; or $ pip install POT") -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Theo Lacombe - - Copyright (C) 2019 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Theo Lacombe +# +# Copyright (C) 2019 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification def _proj_on_diag(X): ''' diff --git a/src/python/gudhi/witness_complex.pyx b/src/python/gudhi/witness_complex.pyx index c19ccf2b..153fc615 100644 --- a/src/python/gudhi/witness_complex.pyx +++ b/src/python/gudhi/witness_complex.pyx @@ -6,15 +6,14 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" -- cgit v1.2.3 From c8be137d15f40f40c4150f0c0fb9776cd680a36c Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 19 Nov 2019 07:33:46 +0100 Subject: Doc review: Add the starting point of the documentation in the Alpha complex reference --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index b3d1e6ea..0c2569c8 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -77,8 +77,19 @@ template struct Is_Epeck_D> { static const bool val * < * CGAL::Dynamic_dimension_tag > * - * \remark When Alpha_complex is constructed with an infinite value of alpha, the complex is a Delaunay complex. - * + * \remark + * - When Alpha_complex is constructed with an infinite value of alpha, the complex is a Delaunay complex. + * - Using the default `CGAL::Epeck_d` makes the construction safe. If you pass exact=true to create_complex, the + * filtration values are the exact ones converted to the filtration value type of the simplicial complex. This can be + * very slow. If you pass exact=false (the default), the filtration values are only guaranteed to have a small + * multiplicative error compared to the exact value, see + * CGAL::Lazy_exact_nt::set_relative_precision_of_to_double for details. A drawback, when computing + * persistence, is that an empty exact interval [10^12,10^12] may become a non-empty approximate interval + * [10^12,10^12+10^6]. Using `CGAL::Epick_d` makes the computations slightly faster, and the combinatorics are still + * exact, but the computation of filtration values can exceptionally be arbitrarily bad. In all cases, we still + * guarantee that the output is a valid filtration (faces have a filtration value no larger than their cofaces). + * - For performances reasons, it is advised to use `Alpha_complex` with \ref cgal ≥ 5.0.0. */ template> class Alpha_complex { -- cgit v1.2.3 From 4d44e27f65488964a3c8af7038d3d9b90e6d5a43 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 19 Nov 2019 17:41:39 +0100 Subject: Add warning in the doc of assign_filtration Apparently some users expect that it will magically "fix" other simplices so it remains a valid filtration. --- src/python/gudhi/simplex_tree.pyx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index ade3bf6c..4a3cd9bc 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -74,13 +74,22 @@ cdef class SimplexTree: return self.get_ptr().simplex_filtration(simplex) def assign_filtration(self, simplex, filtration): - """This function assigns the simplicial complex filtration value for a + """This function assigns a new filtration value to a given N-simplex. :param simplex: The N-simplex, represented by a list of vertex. :type simplex: list of int. - :param filtration: The simplicial complex filtration value. + :param filtration: The new filtration value. :type filtration: float + + .. note:: + Beware that after this operation, the structure may not be a valid + filtration anymore, a simplex could have a lower filtration value + than one of its faces. Callers are responsible for fixing this + (with more :meth:`assign_filtration` or + :meth:`make_filtration_non_decreasing` for instance) before calling + any function that relies on the filtration property, like + :meth:`initialize_filtration`. """ self.get_ptr().assign_simplex_filtration(simplex, filtration) -- cgit v1.2.3 From 57c1f0f4b36b934c8fc54c8691f77b9b6d0eff9d Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 20 Nov 2019 09:36:17 +0100 Subject: Code review : rollback array init --- src/common/include/gudhi/reader_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/include/gudhi/reader_utils.h b/src/common/include/gudhi/reader_utils.h index ee811967..5e96b5db 100644 --- a/src/common/include/gudhi/reader_utils.h +++ b/src/common/include/gudhi/reader_utils.h @@ -305,7 +305,7 @@ void read_persistence_intervals_and_dimension(std::string const& filename, Outpu std::string line; getline(in, line); if (line.length() != 0 && line[0] != '#') { - double numbers[4] = {0.}; + double numbers[4]; int n = sscanf(line.c_str(), "%lf %lf %lf %lf", &numbers[0], &numbers[1], &numbers[2], &numbers[3]); #ifdef DEBUG_TRACES std::cout << "[" << n << "] = " << numbers[0] << "," << numbers[1] -- cgit v1.2.3 From 6bc1f27bbab03139718db674f98d748a7aeaced3 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 20 Nov 2019 14:12:01 +0100 Subject: Use matplotlib axes to be able to subplot persistence graphical tools --- .../doc/persistence_graphical_tools_user.rst | 13 ++-- ...ex_diagram_persistence_from_off_file_example.py | 7 +- ...ex_diagram_persistence_from_off_file_example.py | 7 +- ...ex_diagram_persistence_from_off_file_example.py | 7 +- .../example/gudhi_graphical_tools_example.py | 18 ++++- ...arcode_persistence_from_perseus_file_example.py | 4 +- ...istence_from_correlation_matrix_file_example.py | 7 +- ...ersistence_from_distance_matrix_file_example.py | 7 +- ...ex_diagram_persistence_from_off_file_example.py | 7 +- src/python/example/rips_persistence_diagram.py | 5 +- .../example/sparse_rips_persistence_diagram.py | 5 +- ...complex_plain_homology_from_off_file_example.py | 7 +- src/python/gudhi/persistence_graphical_tools.py | 90 +++++++++++++--------- 13 files changed, 113 insertions(+), 71 deletions(-) diff --git a/src/python/doc/persistence_graphical_tools_user.rst b/src/python/doc/persistence_graphical_tools_user.rst index b2124fdd..2de99252 100644 --- a/src/python/doc/persistence_graphical_tools_user.rst +++ b/src/python/doc/persistence_graphical_tools_user.rst @@ -20,6 +20,7 @@ This function can display the persistence result as a barcode: .. plot:: :include-source: + import matplotlib.pyplot as plot import gudhi off_file = gudhi.__root_source_dir__ + '/data/points/tore3D_300.off' @@ -29,7 +30,7 @@ This function can display the persistence result as a barcode: simplex_tree = rips_complex.create_simplex_tree(max_dimension=3) diag = simplex_tree.persistence(min_persistence=0.4) - plot = gudhi.plot_persistence_barcode(diag) + gudhi.plot_persistence_barcode(diag) plot.show() Show persistence as a diagram @@ -43,14 +44,15 @@ This function can display the persistence result as a diagram: .. plot:: :include-source: + import matplotlib.pyplot as plot import gudhi # rips_on_tore3D_1307.pers obtained from write_persistence_diagram method persistence_file=gudhi.__root_source_dir__ + \ '/data/persistence_diagram/rips_on_tore3D_1307.pers' - plt = gudhi.plot_persistence_diagram(persistence_file=persistence_file, + gudhi.plot_persistence_diagram(persistence_file=persistence_file, legend=True) - plt.show() + plot.show() Persistence density ------------------- @@ -63,11 +65,12 @@ If you want more information on a specific dimension, for instance: .. plot:: :include-source: + import matplotlib.pyplot as plot import gudhi # rips_on_tore3D_1307.pers obtained from write_persistence_diagram method persistence_file=gudhi.__root_source_dir__ + \ '/data/persistence_diagram/rips_on_tore3D_1307.pers' - plt = gudhi.plot_persistence_density(persistence_file=persistence_file, + gudhi.plot_persistence_density(persistence_file=persistence_file, max_intervals=0, dimension=1, legend=True) - plt.show() + plot.show() diff --git a/src/python/example/alpha_complex_diagram_persistence_from_off_file_example.py b/src/python/example/alpha_complex_diagram_persistence_from_off_file_example.py index b8f283b3..4079a469 100755 --- a/src/python/example/alpha_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/alpha_complex_diagram_persistence_from_off_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -60,8 +61,8 @@ with open(args.file, "r") as f: print(simplex_tree.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() else: print(args.file, "is not a valid OFF file") diff --git a/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py b/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py index 610ba44f..0eedd140 100755 --- a/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -75,8 +76,8 @@ with open(args.file, "r") as f: print(simplex_tree.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() else: print(args.file, "is not a valid OFF file") diff --git a/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py b/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py index 7587b732..1fe55737 100755 --- a/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -74,8 +75,8 @@ with open(args.file, "r") as f: print(simplex_tree.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() else: print(args.file, "is not a valid OFF file") diff --git a/src/python/example/gudhi_graphical_tools_example.py b/src/python/example/gudhi_graphical_tools_example.py index 3b0ca54d..37ecbf53 100755 --- a/src/python/example/gudhi_graphical_tools_example.py +++ b/src/python/example/gudhi_graphical_tools_example.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +import matplotlib.pyplot as plot import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. @@ -29,15 +30,24 @@ persistence = [ (0, (0.0, 1.0)), ] gudhi.plot_persistence_barcode(persistence) +plot.show() print("#####################################################################") print("Show diagram persistence example") -pplot = gudhi.plot_persistence_diagram(persistence) -pplot.show() +gudhi.plot_persistence_diagram(persistence) +plot.show() print("#####################################################################") print("Show diagram persistence example with a confidence band") -pplot = gudhi.plot_persistence_diagram(persistence, band=0.2) -pplot.show() +gudhi.plot_persistence_diagram(persistence, band=0.2) +plot.show() + +print("#####################################################################") +print("Show barcode and diagram persistence side by side example") +fig, axes = plot.subplots(nrows=1, ncols=2) +gudhi.plot_persistence_barcode(persistence, axes = axes[0]) +gudhi.plot_persistence_diagram(persistence, axes = axes[1]) +fig.suptitle("barcode versus diagram") +plot.show() diff --git a/src/python/example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py b/src/python/example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py index 9cb855cd..c692e66f 100755 --- a/src/python/example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py +++ b/src/python/example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -70,5 +71,6 @@ if is_file_perseus(args.file): print(periodic_cubical_complex.betti_numbers()) if args.no_barcode == False: gudhi.plot_persistence_barcode(diag) + plot.show() else: print(args.file, "is not a valid perseus style file") diff --git a/src/python/example/rips_complex_diagram_persistence_from_correlation_matrix_file_example.py b/src/python/example/rips_complex_diagram_persistence_from_correlation_matrix_file_example.py index 3571580b..1acb187c 100755 --- a/src/python/example/rips_complex_diagram_persistence_from_correlation_matrix_file_example.py +++ b/src/python/example/rips_complex_diagram_persistence_from_correlation_matrix_file_example.py @@ -1,8 +1,9 @@ #!/usr/bin/env python -import gudhi import sys import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -83,5 +84,5 @@ invert_diag = [ ] if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(invert_diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(invert_diag, band=args.band) + plot.show() diff --git a/src/python/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py b/src/python/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py index 0b9a9ba9..79ccca96 100755 --- a/src/python/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py +++ b/src/python/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -59,5 +60,5 @@ print("betti_numbers()=") print(simplex_tree.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() diff --git a/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py b/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py index 2b335bba..b9074cf9 100755 --- a/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -64,8 +65,8 @@ with open(args.file, "r") as f: print(simplex_tree.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() else: print(args.file, "is not a valid OFF file") diff --git a/src/python/example/rips_persistence_diagram.py b/src/python/example/rips_persistence_diagram.py index f5897d7b..2a90b4bc 100755 --- a/src/python/example/rips_persistence_diagram.py +++ b/src/python/example/rips_persistence_diagram.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +import matplotlib.pyplot as plot import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. @@ -26,5 +27,5 @@ simplex_tree = rips.create_simplex_tree(max_dimension=1) diag = simplex_tree.persistence(homology_coeff_field=2, min_persistence=0) print("diag=", diag) -pplot = gudhi.plot_persistence_diagram(diag) -pplot.show() +gudhi.plot_persistence_diagram(diag) +plot.show() diff --git a/src/python/example/sparse_rips_persistence_diagram.py b/src/python/example/sparse_rips_persistence_diagram.py index 671d5e34..410a6a86 100755 --- a/src/python/example/sparse_rips_persistence_diagram.py +++ b/src/python/example/sparse_rips_persistence_diagram.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +import matplotlib.pyplot as plot import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. @@ -28,5 +29,5 @@ simplex_tree = rips.create_simplex_tree(max_dimension=2) diag = simplex_tree.persistence(homology_coeff_field=2, min_persistence=0) print("diag=", diag) -pplot = gudhi.plot_persistence_diagram(diag) -pplot.show() +gudhi.plot_persistence_diagram(diag) +plot.show() diff --git a/src/python/example/tangential_complex_plain_homology_from_off_file_example.py b/src/python/example/tangential_complex_plain_homology_from_off_file_example.py index 456bc9eb..f0df2189 100755 --- a/src/python/example/tangential_complex_plain_homology_from_off_file_example.py +++ b/src/python/example/tangential_complex_plain_homology_from_off_file_example.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -import gudhi import argparse +import matplotlib.pyplot as plot +import gudhi """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -56,8 +57,8 @@ with open(args.file, "r") as f: print(st.betti_numbers()) if args.no_diagram == False: - pplot = gudhi.plot_persistence_diagram(diag, band=args.band) - pplot.show() + gudhi.plot_persistence_diagram(diag, band=args.band) + plot.show() else: print(args.file, "is not a valid OFF file") diff --git a/src/python/gudhi/persistence_graphical_tools.py b/src/python/gudhi/persistence_graphical_tools.py index 23725ca7..c9dab323 100644 --- a/src/python/gudhi/persistence_graphical_tools.py +++ b/src/python/gudhi/persistence_graphical_tools.py @@ -51,7 +51,8 @@ def plot_persistence_barcode( max_barcodes=1000, inf_delta=0.1, legend=False, - colormap=None + colormap=None, + axes=None ): """This function plots the persistence bar code from persistence values list or from a :doc:`persistence file `. @@ -77,8 +78,10 @@ def plot_persistence_barcode( :param colormap: A matplotlib-like qualitative colormaps. Default is None which means :code:`matplotlib.cm.Set1.colors`. :type colormap: tuple of colors (3-tuple of float between 0. and 1.). - :returns: A matplotlib object containing horizontal bar plot of persistence - (launch `show()` method on it to display it). + :param axes: A matplotlib-like subplot axes. If None, the plot is drawn on + a new set of axes. + :type axes: `matplotlib.axes.Axes` + :returns: (`matplotlib.axes.Axes`): The axes on which the plot was drawn. """ try: import matplotlib.pyplot as plt @@ -112,6 +115,8 @@ def plot_persistence_barcode( if colormap == None: colormap = plt.cm.Set1.colors + if axes == None: + fig, axes = plt.subplots(1, 1) persistence = sorted(persistence, key=lambda birth: birth[1][0]) @@ -126,7 +131,7 @@ def plot_persistence_barcode( for interval in reversed(persistence): if float(interval[1][1]) != float("inf"): # Finite death case - plt.barh( + axes.barh( ind, (interval[1][1] - interval[1][0]), height=0.8, @@ -137,7 +142,7 @@ def plot_persistence_barcode( ) else: # Infinite death case for diagram to be nicer - plt.barh( + axes.barh( ind, (infinity - interval[1][0]), height=0.8, @@ -150,17 +155,19 @@ def plot_persistence_barcode( if legend: dimensions = list(set(item[0] for item in persistence)) - plt.legend( + axes.legend( handles=[ mpatches.Patch(color=colormap[dim], label=str(dim)) for dim in dimensions ], loc="lower right", ) - plt.title("Persistence barcode") + + axes.set_title("Persistence barcode") + # Ends plot on infinity value and starts a little bit before min_birth - plt.axis([axis_start, infinity, 0, ind]) - return plt + axes.axis([axis_start, infinity, 0, ind]) + return axes except ImportError: print("This function is not available, you may be missing matplotlib.") @@ -175,7 +182,8 @@ def plot_persistence_diagram( max_plots=1000, inf_delta=0.1, legend=False, - colormap=None + colormap=None, + axes=None ): """This function plots the persistence diagram from persistence values list or from a :doc:`persistence file `. @@ -203,8 +211,10 @@ def plot_persistence_diagram( :param colormap: A matplotlib-like qualitative colormaps. Default is None which means :code:`matplotlib.cm.Set1.colors`. :type colormap: tuple of colors (3-tuple of float between 0. and 1.). - :returns: A matplotlib object containing diagram plot of persistence - (launch `show()` method on it to display it). + :param axes: A matplotlib-like subplot axes. If None, the plot is drawn on + a new set of axes. + :type axes: `matplotlib.axes.Axes` + :returns: (`matplotlib.axes.Axes`): The axes on which the plot was drawn. """ try: import matplotlib.pyplot as plt @@ -238,6 +248,8 @@ def plot_persistence_diagram( if colormap == None: colormap = plt.cm.Set1.colors + if axes == None: + fig, axes = plt.subplots(1, 1) (min_birth, max_death) = __min_birth_max_death(persistence, band) delta = (max_death - min_birth) * inf_delta @@ -249,18 +261,18 @@ def plot_persistence_diagram( # line display of equation : birth = death x = np.linspace(axis_start, infinity, 1000) # infinity line and text - plt.plot(x, x, color="k", linewidth=1.0) - plt.plot(x, [infinity] * len(x), linewidth=1.0, color="k", alpha=alpha) - plt.text(axis_start, infinity, r"$\infty$", color="k", alpha=alpha) + axes.plot(x, x, color="k", linewidth=1.0) + axes.plot(x, [infinity] * len(x), linewidth=1.0, color="k", alpha=alpha) + axes.text(axis_start, infinity, r"$\infty$", color="k", alpha=alpha) # bootstrap band if band > 0.0: - plt.fill_between(x, x, x + band, alpha=alpha, facecolor="red") + axes.fill_between(x, x, x + band, alpha=alpha, facecolor="red") # Draw points in loop for interval in reversed(persistence): if float(interval[1][1]) != float("inf"): # Finite death case - plt.scatter( + axes.scatter( interval[1][0], interval[1][1], alpha=alpha, @@ -268,25 +280,25 @@ def plot_persistence_diagram( ) else: # Infinite death case for diagram to be nicer - plt.scatter( + axes.scatter( interval[1][0], infinity, alpha=alpha, color=colormap[interval[0]] ) if legend: dimensions = list(set(item[0] for item in persistence)) - plt.legend( + axes.legend( handles=[ mpatches.Patch(color=colormap[dim], label=str(dim)) for dim in dimensions ] ) - plt.title("Persistence diagram") - plt.xlabel("Birth") - plt.ylabel("Death") + axes.set_xlabel("Birth") + axes.set_ylabel("Death") # Ends plot on infinity value and starts a little bit before min_birth - plt.axis([axis_start, infinity, axis_start, infinity + delta]) - return plt + axes.axis([axis_start, infinity, axis_start, infinity + delta]) + axes.set_title("Persistence diagram") + return axes except ImportError: print("This function is not available, you may be missing matplotlib.") @@ -301,6 +313,7 @@ def plot_persistence_density( dimension=None, cmap=None, legend=False, + axes=None ): """This function plots the persistence density from persistence values list or from a :doc:`persistence file `. Be @@ -339,8 +352,10 @@ def plot_persistence_density( :type cmap: cf. matplotlib colormap. :param legend: Display the color bar values (default is False). :type legend: boolean. - :returns: A matplotlib object containing diagram plot of persistence - (launch `show()` method on it to display it). + :param axes: A matplotlib-like subplot axes. If None, the plot is drawn on + a new set of axes. + :type axes: `matplotlib.axes.Axes` + :returns: (`matplotlib.axes.Axes`): The axes on which the plot was drawn. """ try: import matplotlib.pyplot as plt @@ -383,9 +398,15 @@ def plot_persistence_density( birth = persistence_dim[:, 0] death = persistence_dim[:, 1] + # default cmap value cannot be done at argument definition level as matplotlib is not yet defined. + if cmap is None: + cmap = plt.cm.hot_r + if axes == None: + fig, axes = plt.subplots(1, 1) + # line display of equation : birth = death x = np.linspace(death.min(), birth.max(), 1000) - plt.plot(x, x, color="k", linewidth=1.0) + axes.plot(x, x, color="k", linewidth=1.0) # Evaluate a gaussian kde on a regular grid of nbins x nbins over data extents k = kde.gaussian_kde([birth, death], bw_method=bw_method) @@ -395,19 +416,16 @@ def plot_persistence_density( ] zi = k(np.vstack([xi.flatten(), yi.flatten()])) - # default cmap value cannot be done at argument definition level as matplotlib is not yet defined. - if cmap is None: - cmap = plt.cm.hot_r # Make the plot - plt.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=cmap) + axes.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=cmap) if legend: - plt.colorbar() + axes.colorbar() - plt.title("Persistence density") - plt.xlabel("Birth") - plt.ylabel("Death") - return plt + axes.set_xlabel("Birth") + axes.set_ylabel("Death") + axes.set_title("Persistence density") + return axes except ImportError: print( -- cgit v1.2.3 From 7ea7538d02b8f0500dbc31f48dd31fb14d320135 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 20 Nov 2019 20:30:36 +0100 Subject: Minor tweaks to representations uniform -> even matmul -> indexing min+max -> clip --- src/python/gudhi/representations/kernel_methods.py | 2 +- src/python/gudhi/representations/preprocessing.py | 2 +- src/python/gudhi/representations/vector_methods.py | 34 +++++++++++----------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/python/gudhi/representations/kernel_methods.py b/src/python/gudhi/representations/kernel_methods.py index c855d2be..bfc83aff 100644 --- a/src/python/gudhi/representations/kernel_methods.py +++ b/src/python/gudhi/representations/kernel_methods.py @@ -161,7 +161,7 @@ class PersistenceScaleSpaceKernel(BaseEstimator, TransformerMixin): """ Xp = list(X) for i in range(len(Xp)): - op_X = np.matmul(Xp[i], np.array([[0.,1.], [1.,0.]])) + op_X = Xp[i][:,[1,0]] Xp[i] = np.concatenate([Xp[i], op_X], axis=0) return self.pwg_.transform(Xp) diff --git a/src/python/gudhi/representations/preprocessing.py b/src/python/gudhi/representations/preprocessing.py index 83227ca1..487b5376 100644 --- a/src/python/gudhi/representations/preprocessing.py +++ b/src/python/gudhi/representations/preprocessing.py @@ -30,7 +30,7 @@ class BirthPersistenceTransform(BaseEstimator, TransformerMixin): Fit the BirthPersistenceTransform class on a list of persistence diagrams (this function actually does nothing but is useful when BirthPersistenceTransform is included in a scikit-learn Pipeline). Parameters: - X (n x 2 numpy array): input persistence diagrams. + X (list of n x 2 numpy array): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ return self diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py index bf32f18e..61c4fb84 100644 --- a/src/python/gudhi/representations/vector_methods.py +++ b/src/python/gudhi/representations/vector_methods.py @@ -83,7 +83,7 @@ class PersistenceImage(BaseEstimator, TransformerMixin): class Landscape(BaseEstimator, TransformerMixin): """ - This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled uniformly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. + This is a class for computing persistence landscapes from a list of persistence diagrams. A persistence landscape is a collection of 1D piecewise-linear functions computed from the rank function associated to the persistence diagram. These piecewise-linear functions are then sampled evenly on a given range and the corresponding vectors of samples are concatenated and returned. See http://jmlr.org/papers/v16/bubenik15a.html for more details. """ def __init__(self, num_landscapes=5, resolution=100, sample_range=[np.nan, np.nan]): """ @@ -92,7 +92,7 @@ class Landscape(BaseEstimator, TransformerMixin): Parameters: num_landscapes (int): number of piecewise-linear functions to output (default 5). resolution (int): number of sample for all piecewise-linear functions (default 100). - sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn evenly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range @@ -136,9 +136,9 @@ class Landscape(BaseEstimator, TransformerMixin): for j in range(num_pts_in_diag): [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) if min_idx < self.resolution and max_idx > 0: @@ -165,7 +165,7 @@ class Landscape(BaseEstimator, TransformerMixin): class Silhouette(BaseEstimator, TransformerMixin): """ - This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by uniformly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. + This is a class for computing persistence silhouettes from a list of persistence diagrams. A persistence silhouette is computed by taking a weighted average of the collection of 1D piecewise-linear functions given by the persistence landscapes, and then by evenly sampling this average on a given range. Finally, the corresponding vector of samples is returned. See https://arxiv.org/abs/1312.0308 for more details. """ def __init__(self, weight=lambda x: 1, resolution=100, sample_range=[np.nan, np.nan]): """ @@ -174,7 +174,7 @@ class Silhouette(BaseEstimator, TransformerMixin): Parameters: weight (function): weight function for the persistence diagram points (default constant function, ie lambda x: 1). This function must be defined on 2D points, ie on lists or numpy arrays of the form [p_x,p_y]. resolution (int): number of samples for the weighted average (default 100). - sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum for the weighted average domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn evenly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ self.weight, self.resolution, self.sample_range = weight, resolution, sample_range @@ -219,9 +219,9 @@ class Silhouette(BaseEstimator, TransformerMixin): [px,py] = diagram[j,:2] weight = weights[j] / total_weight - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - mid_idx = np.minimum(np.maximum(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) if min_idx < self.resolution and max_idx > 0: @@ -243,7 +243,7 @@ class Silhouette(BaseEstimator, TransformerMixin): class BettiCurve(BaseEstimator, TransformerMixin): """ - This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled uniformly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. + This is a class for computing Betti curves from a list of persistence diagrams. A Betti curve is a 1D piecewise-constant function obtained from the rank function. It is sampled evenly on a given range and the vector of samples is returned. See https://www.researchgate.net/publication/316604237_Time_Series_Classification_via_Topological_Data_Analysis for more details. """ def __init__(self, resolution=100, sample_range=[np.nan, np.nan]): """ @@ -251,7 +251,7 @@ class BettiCurve(BaseEstimator, TransformerMixin): Parameters: resolution (int): number of sample for the piecewise-constant function (default 100). - sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. + sample_range ([double, double]): minimum and maximum of the piecewise-constant function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn evenly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ self.resolution, self.sample_range = resolution, sample_range @@ -290,8 +290,8 @@ class BettiCurve(BaseEstimator, TransformerMixin): bc = np.zeros(self.resolution) for j in range(num_pts_in_diag): [px,py] = diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) for k in range(min_idx, max_idx): bc[k] += 1 @@ -313,7 +313,7 @@ class Entropy(BaseEstimator, TransformerMixin): mode (string): what entropy to compute: either "scalar" for computing the entropy statistics, or "vector" for computing the entropy summary functions (default "scalar"). normalized (bool): whether to normalize the entropy summary function (default True). Used only if **mode** = "vector". resolution (int): number of sample for the entropy summary function (default 100). Used only if **mode** = "vector". - sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn uniformly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". + sample_range ([double, double]): minimum and maximum of the entropy summary function domain, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn evenly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. Used only if **mode** = "vector". """ self.mode, self.normalized, self.resolution, self.sample_range = mode, normalized, resolution, sample_range @@ -359,8 +359,8 @@ class Entropy(BaseEstimator, TransformerMixin): ent = np.zeros(self.resolution) for j in range(num_pts_in_diag): [px,py] = orig_diagram[j,:2] - min_idx = np.minimum(np.maximum(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) - max_idx = np.minimum(np.maximum(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0), self.resolution) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) for k in range(min_idx, max_idx): ent[k] += (-1) * new_diagram[j,1] * np.log(new_diagram[j,1]) if self.normalized: -- cgit v1.2.3 From 96c90dd002e7d4f30ce3d0f3ddf45fbfd03fc01a Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Wed, 20 Nov 2019 22:41:54 +0100 Subject: More tweaks in representations wording for the case where CGAL is missing comparing pointers is sufficient to detect if we are doing fit_transform epsilon is an additive error for bottleneck, so it is unsafe to use 1e-3 or 1e-4 as default --- src/python/gudhi/representations/metrics.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/python/gudhi/representations/metrics.py b/src/python/gudhi/representations/metrics.py index c512cb82..f9f59053 100644 --- a/src/python/gudhi/representations/metrics.py +++ b/src/python/gudhi/representations/metrics.py @@ -86,12 +86,12 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): """ This is a class for computing the bottleneck distance matrix from a list of persistence diagrams. """ - def __init__(self, epsilon=1e-3): + def __init__(self, epsilon=None): """ Constructor for the BottleneckDistance class. Parameters: - epsilon (double): approximation quality (default 1e-4). + epsilon (double): absolute (additive) error tolerated on the distance (default is the smallest positive float), see :func:`bottleneck_distance`. """ self.epsilon = epsilon @@ -118,7 +118,8 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): """ num_diag1 = len(X) - if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + #if len(self.diagrams_) == len(X) and np.all([np.array_equal(self.diagrams_[i], X[i]) for i in range(len(X))]): + if X is self.diagrams_: matrix = np.zeros((num_diag1, num_diag1)) if USE_GUDHI: @@ -127,7 +128,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): matrix[i,j] = bottleneck_distance(X[i], X[j], self.epsilon) matrix[j,i] = matrix[i,j] else: - print("Gudhi required---returning null matrix") + print("Gudhi built without CGAL: returning a null matrix") else: num_diag2 = len(self.diagrams_) @@ -138,7 +139,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): for j in range(num_diag2): matrix[i,j] = bottleneck_distance(X[i], self.diagrams_[j], self.epsilon) else: - print("Gudhi required---returning null matrix") + print("Gudhi built without CGAL: returning a null matrix") Xfit = matrix -- cgit v1.2.3 From 1e3b4f6c56409dde933341701575dc105ede12db Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 21 Nov 2019 09:34:06 +0100 Subject: Code review : review races for read_persistence_intervals_in_dimension function --- src/common/include/gudhi/reader_utils.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/common/include/gudhi/reader_utils.h b/src/common/include/gudhi/reader_utils.h index 5e96b5db..db31bf5c 100644 --- a/src/common/include/gudhi/reader_utils.h +++ b/src/common/include/gudhi/reader_utils.h @@ -293,6 +293,9 @@ Note: the function does not check that birth <= death. **/ template void read_persistence_intervals_and_dimension(std::string const& filename, OutputIterator out) { +#ifdef DEBUG_TRACES + std::cout << "read_persistence_intervals_and_dimension - " << filename << std::endl; +#endif // DEBUG_TRACES std::ifstream in(filename); if (!in.is_open()) { std::string error_str("read_persistence_intervals_and_dimension - Unable to open file "); @@ -308,8 +311,11 @@ void read_persistence_intervals_and_dimension(std::string const& filename, Outpu double numbers[4]; int n = sscanf(line.c_str(), "%lf %lf %lf %lf", &numbers[0], &numbers[1], &numbers[2], &numbers[3]); #ifdef DEBUG_TRACES - std::cout << "[" << n << "] = " << numbers[0] << "," << numbers[1] - << "," << numbers[2] << "," << numbers[3] << std::endl; + std::cout << "[" << n << "] = "; + for (int i = 0; i < n; i++) { + std::cout << numbers[i] << ","; + } + std::cout << std::endl; #endif // DEBUG_TRACES if (n >= 2) { int dim = (n >= 3 ? static_cast(numbers[n - 3]) : -1); -- cgit v1.2.3 From 84299342910d18aeaa1a605fe224bede13d647dc Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 21 Nov 2019 22:50:05 +0100 Subject: Generalize Clamping to min and max Fix #146. --- src/python/example/diagram_vectorizations_distances_kernels.py | 2 +- src/python/gudhi/representations/preprocessing.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py index a77bbfdd..119072eb 100755 --- a/src/python/example/diagram_vectorizations_distances_kernels.py +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -16,7 +16,7 @@ diags = [D] diags = DiagramSelector(use=True, point_type="finite").fit_transform(diags) diags = DiagramScaler(use=True, scalers=[([0,1], MinMaxScaler())]).fit_transform(diags) -diags = DiagramScaler(use=True, scalers=[([1], Clamping(limit=.9))]).fit_transform(diags) +diags = DiagramScaler(use=True, scalers=[([1], Clamping(maximum=.9))]).fit_transform(diags) D = diags[0] plt.scatter(D[:,0],D[:,1]) diff --git a/src/python/gudhi/representations/preprocessing.py b/src/python/gudhi/representations/preprocessing.py index 487b5376..a39b00e4 100644 --- a/src/python/gudhi/representations/preprocessing.py +++ b/src/python/gudhi/representations/preprocessing.py @@ -58,14 +58,15 @@ class Clamping(BaseEstimator, TransformerMixin): """ This is a class for clamping values. It can be used as a parameter for the DiagramScaler class, for instance if you want to clamp abscissae or ordinates of persistence diagrams. """ - def __init__(self, limit=np.inf): + def __init__(self, minimum=-np.inf, maximum=np.inf): """ Constructor for the Clamping class. Parameters: limit (double): clamping value (default np.inf). """ - self.limit = limit + self.minimum = minimum + self.maximum = maximum def fit(self, X, y=None): """ @@ -87,8 +88,7 @@ class Clamping(BaseEstimator, TransformerMixin): Returns: numpy array of size n: output list of values. """ - Xfit = np.minimum(X, self.limit) - #Xfit = np.where(X >= self.limit, self.limit * np.ones(X.shape), X) + Xfit = np.clip(X, self.minimum, self.maximum) return Xfit class DiagramScaler(BaseEstimator, TransformerMixin): -- cgit v1.2.3 From da22cbc891b2b7a9b326e3840533b4d3b49a26d3 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 22 Nov 2019 11:50:34 +0100 Subject: Fix link to bottleneck_distance. --- src/python/gudhi/representations/metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/gudhi/representations/metrics.py b/src/python/gudhi/representations/metrics.py index f9f59053..5f9ec6ab 100644 --- a/src/python/gudhi/representations/metrics.py +++ b/src/python/gudhi/representations/metrics.py @@ -91,7 +91,7 @@ class BottleneckDistance(BaseEstimator, TransformerMixin): Constructor for the BottleneckDistance class. Parameters: - epsilon (double): absolute (additive) error tolerated on the distance (default is the smallest positive float), see :func:`bottleneck_distance`. + epsilon (double): absolute (additive) error tolerated on the distance (default is the smallest positive float), see :func:`gudhi.bottleneck_distance`. """ self.epsilon = epsilon -- cgit v1.2.3 From d2d22e40e9ae720accbc027bb86d3f7ec7d721de Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 15:29:37 +0100 Subject: Simplify and boldify main pages (modules, installation, examples, ...) in the left top menu of sphinx documentation --- src/python/doc/_templates/layout.html | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/python/doc/_templates/layout.html b/src/python/doc/_templates/layout.html index fe64fb3d..2f2d9c72 100644 --- a/src/python/doc/_templates/layout.html +++ b/src/python/doc/_templates/layout.html @@ -56,12 +56,16 @@

{%- endif %} {%- endblock %} -

GUDHI

-

File formats

-

GUDHI installation

-

Acknowledging the GUDHI library

-

Index

-

Examples

+ + + {%- if sidebars != None %} {#- new style sidebar: explicitly include/exclude templates #} {%- for sidebartemplate in sidebars %} -- cgit v1.2.3 From 0bceb28453a10d94571ff7adff8ddbe19cae0eba Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 15:30:22 +0100 Subject: Fix typo - module*s* require plural form --- src/python/doc/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/doc/index.rst b/src/python/doc/index.rst index 1ef08096..c36a578f 100644 --- a/src/python/doc/index.rst +++ b/src/python/doc/index.rst @@ -1,5 +1,5 @@ -GUDHI Python module documentation -################################# +GUDHI Python modules documentation +################################## .. figure:: ../../doc/common/Gudhi_banner.png -- cgit v1.2.3 From eefdb67e436ba3b881c06b8248d169209dd7016c Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 16:19:36 +0100 Subject: Add information about *make python* and *make clean* target. It does not fix, but workaround issues #88 #89 and #90 --- src/python/doc/installation.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 7699a5bb..54504413 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -40,6 +40,20 @@ To build the GUDHI Python module, run the following commands in a terminal: cd python make +.. note:: + + :code:`make python` (or :code:`make` in python directory) is only a + `CMake custom targets `_ + to shortcut :code:`python setup.py build_ext --inplace` command. + No specific other options (:code:`-j8` for parallel, or even :code:`make clean`, ...) are + available. + But one can use :code:`python setup.py ...` specific options in the python directory: + +.. code-block:: bash + + python setup.py clean --all # Clean former compilation + python setup.py build_ext -j 8 --inplace # Build in parallel + GUDHI Python module installation ================================ @@ -59,6 +73,17 @@ Or install it definitely in your Python packages folder: # May require sudo or administrator privileges make install +.. note:: + + :code:`make install` is only a + `CMake custom targets `_ + to shortcut :code:`python setup.py install` command. + It does not take into account :code:`CMAKE_INSTALL_PREFIX`. + But one can use :code:`python setup.py install ...` specific options in the python directory: + +.. code-block:: bash + + python setup.py install --prefix /home/gudhi # Install in /home/gudhi directory Test suites =========== -- cgit v1.2.3 From b952302afa204e2b9e23ad503043655b2a0971b0 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 17:28:46 +0100 Subject: Fix #142: Bottleneck.h already have such a check and CGAL dependency is only due to Bottleneck --- src/Nerve_GIC/include/gudhi/GIC.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Nerve_GIC/include/gudhi/GIC.h b/src/Nerve_GIC/include/gudhi/GIC.h index fc6a2a91..b8169c59 100644 --- a/src/Nerve_GIC/include/gudhi/GIC.h +++ b/src/Nerve_GIC/include/gudhi/GIC.h @@ -48,11 +48,6 @@ #include #include -// Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 -#if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 -#endif - namespace Gudhi { namespace cover_complex { -- cgit v1.2.3 From 85cab5aeff0407e1a8095328527e00b94f2796c6 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 17:29:55 +0100 Subject: Error message in #10 fix was not correct, bad copy/paste --- src/Bottleneck_distance/include/gudhi/Bottleneck.h | 2 +- src/Contraction/include/gudhi/Skeleton_blocker_contractor.h | 2 +- src/Spatial_searching/include/gudhi/Kd_tree_search.h | 4 ++-- src/Tangential_complex/include/gudhi/Tangential_complex.h | 4 ++-- src/Witness_complex/include/gudhi/Euclidean_strong_witness_complex.h | 4 ++-- src/Witness_complex/include/gudhi/Euclidean_witness_complex.h | 4 ++-- src/common/include/gudhi/random_point_generators.h | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Bottleneck_distance/include/gudhi/Bottleneck.h b/src/Bottleneck_distance/include/gudhi/Bottleneck.h index 82ba9f68..e466828a 100644 --- a/src/Bottleneck_distance/include/gudhi/Bottleneck.h +++ b/src/Bottleneck_distance/include/gudhi/Bottleneck.h @@ -26,7 +26,7 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error bottleneck_distance is only available for CGAL >= 4.11 #endif namespace Gudhi { diff --git a/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h b/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h index c2b3157c..a0d9f2b2 100644 --- a/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h +++ b/src/Contraction/include/gudhi/Skeleton_blocker_contractor.h @@ -40,7 +40,7 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Skeleton_blocker_contractor is only available for CGAL >= 4.11 #endif namespace Gudhi { diff --git a/src/Spatial_searching/include/gudhi/Kd_tree_search.h b/src/Spatial_searching/include/gudhi/Kd_tree_search.h index fedbb32e..87969dd9 100644 --- a/src/Spatial_searching/include/gudhi/Kd_tree_search.h +++ b/src/Spatial_searching/include/gudhi/Kd_tree_search.h @@ -30,11 +30,11 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Kd_tree_search is only available for CGAL >= 4.11 #endif #if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL +# error Kd_tree_search is only available for Eigen3 >= 3.1.0 installed with CGAL #endif namespace Gudhi { diff --git a/src/Tangential_complex/include/gudhi/Tangential_complex.h b/src/Tangential_complex/include/gudhi/Tangential_complex.h index f59476b1..f058fa9f 100644 --- a/src/Tangential_complex/include/gudhi/Tangential_complex.h +++ b/src/Tangential_complex/include/gudhi/Tangential_complex.h @@ -67,11 +67,11 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Tangential_complex is only available for CGAL >= 4.11 #endif #if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL +# error Tangential_complex is only available for Eigen3 >= 3.1.0 installed with CGAL #endif namespace sps = Gudhi::spatial_searching; diff --git a/src/Witness_complex/include/gudhi/Euclidean_strong_witness_complex.h b/src/Witness_complex/include/gudhi/Euclidean_strong_witness_complex.h index 7d3c2d6d..4d5e73f2 100644 --- a/src/Witness_complex/include/gudhi/Euclidean_strong_witness_complex.h +++ b/src/Witness_complex/include/gudhi/Euclidean_strong_witness_complex.h @@ -25,11 +25,11 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Euclidean_strong_witness_complex is only available for CGAL >= 4.11 #endif #if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL +# error Euclidean_strong_witness_complex is only available for Eigen3 >= 3.1.0 installed with CGAL #endif namespace Gudhi { diff --git a/src/Witness_complex/include/gudhi/Euclidean_witness_complex.h b/src/Witness_complex/include/gudhi/Euclidean_witness_complex.h index 21682ec4..09cb7ec2 100644 --- a/src/Witness_complex/include/gudhi/Euclidean_witness_complex.h +++ b/src/Witness_complex/include/gudhi/Euclidean_witness_complex.h @@ -27,11 +27,11 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error Euclidean_witness_complex is only available for CGAL >= 4.11 #endif #if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL +# error Euclidean_witness_complex is only available for Eigen3 >= 3.1.0 installed with CGAL #endif namespace Gudhi { diff --git a/src/common/include/gudhi/random_point_generators.h b/src/common/include/gudhi/random_point_generators.h index fb69f832..9dd88ac4 100644 --- a/src/common/include/gudhi/random_point_generators.h +++ b/src/common/include/gudhi/random_point_generators.h @@ -21,7 +21,7 @@ // Make compilation fail - required for external projects - https://github.com/GUDHI/gudhi-devel/issues/10 #if CGAL_VERSION_NR < 1041101000 -# error Alpha_complex_3d is only available for CGAL >= 4.11 +# error random_point_generators is only available for CGAL >= 4.11 #endif namespace Gudhi { -- cgit v1.2.3 From 52239c9c342da1f04ab69dd5a9ad0c3135ea5091 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 23:34:54 +0100 Subject: Update cgal bibtex --- biblio/how_to_cite_cgal.bib | 920 +++++++++++++++++++++++++------------------- 1 file changed, 515 insertions(+), 405 deletions(-) diff --git a/biblio/how_to_cite_cgal.bib b/biblio/how_to_cite_cgal.bib index 7336ee81..9e3b69e5 100644 --- a/biblio/how_to_cite_cgal.bib +++ b/biblio/how_to_cite_cgal.bib @@ -1,947 +1,1057 @@ -@book{ cgal:eb-15b +@book{ cgal:eb-19b , title = "{CGAL} User and Reference Manual" , author = "{The CGAL Project}" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" -, year = 2015 -, url = "http://doc.cgal.org/4.7/Manual/packages.html" +, edition = "{5.0}" +, year = 2019 +, url = "https://doc.cgal.org/5.0/Manual/packages.html" } -@incollection{cgal:h-af-15b +@incollection{cgal:h-af-19b , author = "Michael Hemmer" , title = "Algebraic Foundations" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgAlgebraicFoundationsSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgAlgebraicFoundations" +, year = 2019 } -@incollection{cgal:hhkps-nt-15b +@incollection{cgal:hhkps-nt-19b , author = "Michael Hemmer and Susan Hert and Sylvain Pion and Stefan Schirra" , title = "Number Types" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgNumberTypesSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgNumberTypes" +, year = 2019 } -@incollection{cgal:h-ma-15b +@incollection{cgal:h-ma-19b , author = "Michael Hemmer and Sylvain Pion" , title = "Modular Arithmetic" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgModularArithmeticSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgModularArithmetic" +, year = 2019 } -@incollection{cgal:h-p-15b +@incollection{cgal:h-p-19b , author = "Michael Hemmer" , title = "Polynomial" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPolynomialSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPolynomial" +, year = 2019 } -@incollection{cgal:bht-ak-15b +@incollection{cgal:bht-ak-19b , author = "Eric Berberich and Michael Hemmer and Michael Kerber and Sylvain Lazard and Luis Pe{\~n}aranda and Monique Teillaud" , title = "Algebraic Kernel" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgAlgebraicKerneldSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgAlgebraicKernelD" +, year = 2019 } -@incollection{cgal:h-msms-15b +@incollection{cgal:h-msms-19b , author = "Michael Hoffmann" , title = "Monotone and Sorted Matrix Search" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgMatrixSearchSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgMatrixSearch" +, year = 2019 } -@incollection{cgal:fgsw-lqps-15b +@incollection{cgal:fgsw-lqps-19b , author = "Kaspar Fischer and Bernd G{\"a}rtner and Sven Sch{\"o}nherr and Frans Wessendorp" , title = "Linear and Quadratic Programming Solver" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgQPSolverSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgQPSolver" +, year = 2019 } -@incollection{cgal:bfghhkps-lgk23-15b +@incollection{cgal:bfghhkps-lgk23-19b , author = "Herv{\'e} Br{\"o}nnimann and Andreas Fabri and Geert-Jan Giezeman and Susan Hert and Michael Hoffmann and Lutz Kettner and Sylvain Pion and Stefan Schirra" , title = "{2D} and {3D} Linear Geometry Kernel" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgKernel23Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgKernel23" +, year = 2019 } -@incollection{cgal:s-gkd-15b +@incollection{cgal:s-gkd-19b , author = "Michael Seel" , title = "{dD} Geometry Kernel" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgKernelDSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgKernelD" +, year = 2019 } -@incollection{cgal:cpt-cgk2-15b +@incollection{cgal:cpt-cgk2-19b , author = "Pedro Machado Manh{\~a}es de Castro and Sylvain Pion and Monique Teillaud" , title = "{2D} Circular Geometry Kernel" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgCircularKernel2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgCircularKernel2" +, year = 2019 } -@incollection{cgal:cclt-sgk3-15b +@incollection{cgal:cclt-sgk3-19b , author = "Pedro Machado Manh{\~a}es de Castro and Fr{\'e}d{\'e}ric Cazals and S{\'e}bastien Loriot and Monique Teillaud" , title = "{3D} Spherical Geometry Kernel" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSphericalKernel3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgCircularKernel3" +, year = 2019 } -@incollection{cgal:hs-chep2-15b +@incollection{cgal:hs-chep2-19b , author = "Susan Hert and Stefan Schirra" , title = "{2D} Convex Hulls and Extreme Points" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgConvexHull2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgConvexHull2" +, year = 2019 } -@incollection{cgal:hs-ch3-15b +@incollection{cgal:hs-ch3-19b , author = "Susan Hert and Stefan Schirra" , title = "{3D} Convex Hulls" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgConvexHull3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgConvexHull3" +, year = 2019 } -@incollection{cgal:hs-chdt3-15b +@incollection{cgal:hs-chdt3-19b , author = "Susan Hert and Michael Seel" , title = "{dD} Convex Hulls and Delaunay Triangulations" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgConvexHullDSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgConvexHullD" +, year = 2019 } -@incollection{cgal:gw-p2-15b +@incollection{cgal:gw-p2-19b , author = "Geert-Jan Giezeman and Wieger Wesselink" , title = "{2D} Polygons" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPolygon2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPolygon2" +, year = 2019 } -@incollection{cgal:fwzh-rbso2-15b +@incollection{cgal:fwzh-rbso2-19b , author = "Efi Fogel and Ophir Setter and Ron Wein and Guy Zucker and Baruch Zukerman and Dan Halperin" , title = "{2D} Regularized Boolean Set-Operations" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgBooleanSetOperations2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgBooleanSetOperations2" +, year = 2019 } -@incollection{cgal:s-bonp2-15b +@incollection{cgal:s-bonp2-19b , author = "Michael Seel" , title = "{2D} Boolean Operations on Nef Polygons" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgNef2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgNef2" +, year = 2019 } -@incollection{cgal:hk-bonpes2-15b +@incollection{cgal:hk-bonpes2-19b , author = "Peter Hachenberger and Lutz Kettner" , title = "{2D} Boolean Operations on Nef Polygons Embedded on the Sphere" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgNefS2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgNefS2" +, year = 2019 } -@incollection{cgal:h-pp2-15b +@incollection{cgal:h-pp2-19b , author = "Susan Hert" , title = "{2D} Polygon Partitioning" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPolygonPartitioning2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPartition2" +, year = 2019 } -@incollection{cgal:c-sspo2-15b +@incollection{cgal:c-sspo2-19b , author = "Fernando Cacciola" , title = "{2D} Straight Skeleton and Polygon Offsetting" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgStraightSkeleton2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgStraightSkeleton2" +, year = 2019 } -@incollection{cgal:w-rms2-15b +@incollection{cgal:w-rms2-19b , author = "Ron Wein and Alon Baram and Eyal Flato and Efi Fogel and Michael Hemmer and Sebastian Morr" , title = "{2D} Minkowski Sums" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgMinkowskiSum2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgMinkowskiSum2" +, year = 2019 } -@incollection{cgal:f-ps2-15b +@incollection{cgal:f-ps2-19b , author = "Andreas Fabri" , title = "{2D} Polyline Simplification" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPolylineSimplification2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPolylineSimplification2" +, year = 2019 } -@incollection{hhb-visibility-2-15b +@incollection{hhb-visibility-2-19b , author = "Michael Hemmer and Kan Huang and Francisc Bungiu and Ning Xu" , title = "{2D} Visibility Computation" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgVisibility_2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgVisibility2" +, year = 2019 } -@incollection{cgal:k-ps-15b +@incollection{cgal:sf-sms2-19b +, author = "Shahar Shamai and Efi Fogel" +, title = "{2D} Movable Separability of Sets" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSetMovableSeparability2" +, year = 2019 +} + +@incollection{cgal:k-ps-19b , author = "Lutz Kettner" , title = "{3D} Polyhedral Surface" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPolyhedronSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPolyhedron" +, year = 2019 } -@incollection{cgal:k-hds-15b +@incollection{cgal:k-hds-19b , author = "Lutz Kettner" , title = "Halfedge Data Structures" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgHDSSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgHalfedgeDS" +, year = 2019 } -@incollection{cgal:bsmf-sm-15b +@incollection{cgal:bsmf-sm-19b , author = "Mario Botsch and Daniel Sieger and Philipp Moeller and Andreas Fabri" , title = "Surface Mesh" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSurfaceMeshSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSurfaceMesh" +, year = 2019 } -@incollection{cgal:d-cm-15b +@incollection{cgal:d-cm-19b , author = "Guillaume Damiand" , title = "Combinatorial Maps" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgCombinatorialMaps" +, year = 2019 +} + +@incollection{cgal:d-gm-19b +, author = "Guillaume Damiand" +, title = "Generalized Maps" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgCombinatorialMapsSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgGeneralizedMaps" +, year = 2019 } -@incollection{cgal:d-lcc-12-15b +@incollection{cgal:d-lcc-12-19b , author = "Guillaume Damiand" , title = "Linear Cell Complex" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgLinearCellComplexSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgLinearCellComplex" +, year = 2019 } -@incollection{cgal:hk-bonp3-15b +@incollection{cgal:hk-bonp3-19b , author = "Peter Hachenberger and Lutz Kettner" , title = "{3D} Boolean Operations on Nef Polyhedra" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgNef3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgNef3" +, year = 2019 } -@incollection{cgal:h-emspe-15b +@incollection{cgal:h-emspe-19b , author = "Peter Hachenberger" , title = "Convex Decomposition of Polyhedra" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgConvexDecomposition3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgConvexDecomposition3" +, year = 2019 } -@incollection{cgal:h-msp3-15b +@incollection{cgal:h-msp3-19b , author = "Peter Hachenberger" , title = "{3D} Minkowski Sum of Polyhedra" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgMinkowskiSum3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgMinkowskiSum3" +, year = 2019 } -@incollection{cgal:wfzh-a2-15b +@incollection{cgal:wfzh-a2-19b , author = "Ron Wein and Eric Berberich and Efi Fogel and Dan Halperin and Michael Hemmer and Oren Salzman and Baruch Zukerman" , title = "{2D} Arrangements" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgArrangement2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgArrangementOnSurface2" +, year = 2019 } -@incollection{cgal:wfz-ic2-15b +@incollection{cgal:wfz-ic2-19b , author = "Baruch Zukerman and Ron Wein and Efi Fogel" , title = "{2D} Intersection of Curves" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgIntersectionOfCurves2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSurfaceSweep2" +, year = 2019 } -@incollection{cgal:p-sr2-15b +@incollection{cgal:p-sr2-19b , author = "Eli Packer" , title = "{2D} Snap Rounding" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSnapRounding2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSnapRounding2" +, year = 2019 } -@incollection{cgal:w-e2-15b +@incollection{cgal:w-e2-19b , author = "Ron Wein" , title = "{2D} Envelopes" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgEnvelope2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgEnvelope2" +, year = 2019 } -@incollection{cgal:mwz-e3-15b +@incollection{cgal:mwz-e3-19b , author = "Dan Halperin and Michal Meyerovitch and Ron Wein and Baruch Zukerman" , title = "{3D} Envelopes" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgEnvelope3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgEnvelope3" +, year = 2019 } -@incollection{cgal:y-t2-15b +@incollection{cgal:y-t2-19b , author = "Mariette Yvinec" , title = "{2D} Triangulation" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgTriangulation2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgTriangulation2" +, year = 2019 } -@incollection{cgal:py-tds2-15b +@incollection{cgal:py-tds2-19b , author = "Sylvain Pion and Mariette Yvinec" , title = "{2D} Triangulation Data Structure" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgTDS2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgTDS2" +, year = 2019 } -@incollection{cgal:k-pt2-13-15b +@incollection{cgal:k-pt2-13-19b , author = "Nico Kruithof" , title = "{2D} Periodic Triangulations" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPeriodic2Triangulation2" +, year = 2019 +} + +@incollection{cgal:bt-ht2-17-19b +, author = "Mikhail Bogdanov and Iordan Iordanov and Monique Teillaud" +, title = "{2D} Hyperbolic Delaunay Triangulations" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgHyperbolicTriangulation2" +, year = 2019 +} + +@incollection{cgal:i-p4ht2-17-19b +, author = "Iordan Iordanov and Monique Teillaud" +, title = "{2D} Periodic Hyperbolic Triangulations" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPeriodic2Triangulation2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPeriodic4HyperbolicTriangulation2" +, year = 2019 } -@incollection{cgal:pt-t3-15b +@incollection{cgal:pt-t3-19b , author = "Cl{\'e}ment Jamin and Sylvain Pion and Monique Teillaud" , title = "{3D} Triangulations" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgTriangulation3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgTriangulation3" +, year = 2019 } -@incollection{cgal:pt-tds3-15b +@incollection{cgal:pt-tds3-19b , author = "Cl{\'e}ment Jamin and Sylvain Pion and Monique Teillaud" , title = "{3D} Triangulation Data Structure" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgTDS3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgTDS3" +, year = 2019 } -@incollection{cgal:ct-pt3-15b -, author = "Manuel Caroli and Monique Teillaud" +@incollection{cgal:ct-pt3-19b +, author = "Manuel Caroli and Aymeric Pell{\'e} and Mael Rouxel-Labb{\'e} and Monique Teillaud" , title = "{3D} Periodic Triangulations" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPeriodic3Triangulation3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPeriodic3Triangulation3" +, year = 2019 } -@incollection{cgal:hdj-t-15b -, author = "Samuel Hornus and Olivier Devillers and Cl{\'e}ment Jamin" +@incollection{cgal:hdj-t-19b +, author = "Olivier Devillers and Samuel Hornus and Cl{\'e}ment Jamin" , title = "{dD} Triangulations" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgTriangulationsSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgTriangulations" +, year = 2019 } -@incollection{cgal:d-as2-15b +@incollection{cgal:d-as2-19b , author = "Tran Kai Frank Da" , title = "{2D} Alpha Shapes" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgAlphaShape2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgAlphaShapes2" +, year = 2019 } -@incollection{cgal:dy-as3-15b +@incollection{cgal:dy-as3-19b , author = "Tran Kai Frank Da and S{\'e}bastien Loriot and Mariette Yvinec" , title = "{3D} Alpha Shapes" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgAlphaShapes3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgAlphaShapes3" +, year = 2019 } -@incollection{cgal:k-sdg2-15b +@incollection{cgal:k-sdg2-19b , author = "Menelaos Karavelas" , title = "{2D} Segment Delaunay Graphs" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSegmentDelaunayGraph2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSegmentDelaunayGraph2" +, year = 2019 } -@incollection{cgal:cdp-sdglinf2-15b +@incollection{cgal:cdp-sdglinf2-19b , author = "Panagiotis Cheilaris and Sandeep Kumar Dey and Evanthia Papadopoulou" , title = "L Infinity Segment Delaunay Graphs" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSDGLinfSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSegmentDelaunayGraphLinf2" +, year = 2019 } -@incollection{cgal:ky-ag2-15b +@incollection{cgal:ky-ag2-19b , author = "Menelaos Karavelas and Mariette Yvinec" , title = "{2D} Apollonius Graphs (Delaunay Graphs of Disks)" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgApolloniusGraph2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgApolloniusGraph2" +, year = 2019 } -@incollection{cgal:k-vda2-15b +@incollection{cgal:k-vda2-19b , author = "Menelaos Karavelas" , title = "{2D} Voronoi Diagram Adaptor" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgVoronoiDiagramAdaptor2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgVoronoiDiagram2" +, year = 2019 } -@incollection{cgal:r-ctm2-15b +@incollection{cgal:r-ctm2-19b , author = "Laurent Rineau" , title = "{2D} Conforming Triangulations and Meshes" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgMesh2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgMesh2" +, year = 2019 } -@incollection{cgal:ry-smg-15b +@incollection{cgal:ry-smg-19b , author = "Laurent Rineau and Mariette Yvinec" , title = "{3D} Surface Mesh Generation" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSurfaceMesher3" +, year = 2019 +} + +@incollection{cgal:k-ssm3-19b +, author = "Nico Kruithof" +, title = "{3D} Skin Surface Meshing" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSkinSurface3" +, year = 2019 +} + +@incollection{cgal:rty-m3-19b +, author = "Pierre Alliez and Cl{\'e}ment Jamin and Laurent Rineau and St{\'e}phane Tayeb and Jane Tournois and Mariette Yvinec" +, title = "{3D} Mesh Generation" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgMesh3" +, year = 2019 +} + +@incollection{cgal:btprl-p3m3-19b +, author = "Mikhail Bogdanov and Aymeric Pell{\'e} and Mael Rouxel-Labb{\'e} and Monique Teillaud" +, title = "{3D} Periodic Mesh Generation" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSurfaceMesher3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPeriodic3Mesh3" +, year = 2019 } -@incollection{cgal:asg-srps-15b +@incollection{cgal:asg-srps-19b , author = "Pierre Alliez and Laurent Saboret and Ga{\"e}l Guennebaud" -, title = "Surface Reconstruction from Point Sets" +, title = "Poisson Surface Reconstruction" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSurfaceReconstructionFromPointSetsSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPoissonSurfaceReconstruction3" +, year = 2019 } -@incollection{cgal:ssr3-15b +@incollection{cgal:ssr3-19b , author = "Thijs van Lankveld" , title = "Scale-Space Surface Reconstruction" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgScaleSpaceReconstruction3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgScaleSpaceReconstruction3" +, year = 2019 } -@incollection{cgal:dc-afsr-15b +@incollection{cgal:dc-afsr-19b , author = "Tran Kai Frank Da and David Cohen-Steiner" , title = "Advancing Front Surface Reconstruction" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgAdvancingFrontSurfaceReconstructionSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgAdvancingFrontSurfaceReconstruction" +, year = 2019 } -@incollection{cgal:k-ssm3-15b -, author = "Nico Kruithof" -, title = "{3D} Skin Surface Meshing" +@incollection{cgal:x-x-19b +, author = "Liangliang Nan" +, title = "Polygonal Surface Reconstruction" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSkinSurface3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPolygonalSurfaceReconstruction" +, year = 2019 } -@incollection{cgal:rty-m3-15b -, author = "Pierre Alliez and Cl{\'e}ment Jamin and Laurent Rineau and St{\'e}phane Tayeb and Jane Tournois and Mariette Yvinec" -, title = "{3D} Mesh Generation" +@incollection{cgal:gavj-rs-19b +, author = "Pierre Alliez and David Cohen-Steiner and Fernando de Goes and Cl{\'e}ment Jamin and Ivo Vigan" +, title = "Optimal Transportation Curve Reconstruction" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgMesh_3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgOptimalTransportationReconstruction2" +, year = 2019 } -@incollection{cgal:lty-pmp-15b -, author = "S{\'e}bastien Loriot and Jane Tournois and Ilker O. Yaz" +@incollection{cgal:lty-pmp-19b +, author = "S{\'e}bastien Loriot and Mael Rouxel-Labb{\'e} and Jane Tournois and Ilker O. Yaz" , title = "Polygon Mesh Processing" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPolygonMeshProcessingSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPolygonMeshProcessing" +, year = 2019 } -@incollection{cgal:s-ssm2-15b +@incollection{cgal:s-ssm2-19b , author = "Le-Jeng Andy Shiue" , title = "{3D} Surface Subdivision Methods" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSurfaceSubdivisionMethods3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSurfaceSubdivisionMethod3" +, year = 2019 } -@incollection{cgal:y-smsimpl-15b +@incollection{cgal:y-smsimpl-19b , author = "Ilker O. Yaz and S{\'e}bastien Loriot" , title = "Triangulated Surface Mesh Segmentation" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSurfaceSegmentationSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSurfaceMeshSegmentation" +, year = 2019 } -@incollection{cgal:c-tsms-12-15b +@incollection{cgal:c-tsms-12-19b , author = "Fernando Cacciola" , title = "Triangulated Surface Mesh Simplification" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSurfaceMeshSimplificationSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSurfaceMeshSimplification" +, year = 2019 } -@incollection{cgal:lsxy-tsmd-15b +@incollection{cgal:lsxy-tsmd-19b , author = "S{\'e}bastien Loriot and Olga Sorkine-Hornung and Yin Xu and Ilker O. Yaz" , title = "Triangulated Surface Mesh Deformation" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSurfaceModelingSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSurfaceMeshDeformation" +, year = 2019 } -@incollection{cgal:sal-pptsm2-15b -, author = "Laurent Saboret and Pierre Alliez and Bruno L{\'e}vy" -, title = "Planar Parameterization of Triangulated Surface Meshes" +@incollection{cgal:salf-pptsm2-19b +, author = "Laurent Saboret and Pierre Alliez and Bruno L{\'e}vy and Mael Rouxel-Labb{\'e} and Andreas Fabri" +, title = "Triangulated Surface Mesh Parameterization" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSurfaceParameterizationSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSurfaceMeshParameterization" +, year = 2019 } -@incollection{cgal:klcdv-tsmsp-15b +@incollection{cgal:klcdv-tsmsp-19b , author = "Stephen Kiazyk and S{\'e}bastien Loriot and {\'E}ric Colin de Verdi{\`e}re" , title = "Triangulated Surface Mesh Shortest Paths" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSurfaceMeshShortestPathSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSurfaceMeshShortestPath" +, year = 2019 } -@incollection{cgal:glt-tsms-15b +@incollection{cgal:glt-tsms-19b , author = "Xiang Gao and S{\'e}bastien Loriot and Andrea Tagliasacchi" , title = "Triangulated Surface Mesh Skeletonization" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgMeanCurvatureSkeleton3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSurfaceMeshSkeletonization" +, year = 2019 } -@incollection{cgal:cp-arutsm-15b +@incollection{cgal:az-tsma-19b +, author = "Pierre Alliez and David Cohen-Steiner and Lingjie Zhu" +, title = "Triangulated Surface Mesh Approximation" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgTSMA" +, year = 2019 +} + +@incollection{cgal:cp-arutsm-19b , author = "Marc Pouget and Fr{\'e}d{\'e}ric Cazals" , title = "Approximation of Ridges and Umbilics on Triangulated Surface Meshes" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgRidges_3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgRidges3" +, year = 2019 } -@incollection{cgal:pc-eldp-15b +@incollection{cgal:pc-eldp-19b , author = "Marc Pouget and Fr{\'e}d{\'e}ric Cazals" , title = "Estimation of Local Differential Properties of Point-Sampled Surfaces" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgJet_fitting_3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgJetFitting3" +, year = 2019 } -@incollection{cgal:ass-psp-15b -, author = "Pierre Alliez and Cl{\'e}ment Jamin and Quentin M{\'e}rigot and Jocelyn Meyron and Laurent Saboret and Nader Salman and Shihao Wu" +@incollection{cgal:g-ps-19b +, author = "Simon Giraudot" +, title = "{3D} Point Set" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPointSet3" +, year = 2019 +} + +@incollection{cgal:ass-psp-19b +, author = "Pierre Alliez and Simon Giraudot and Cl{\'e}ment Jamin and Florent Lafarge and Quentin M{\'e}rigot and Jocelyn Meyron and Laurent Saboret and Nader Salman and Shihao Wu" , title = "Point Set Processing" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPointSetProcessingSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPointSetProcessing3" +, year = 2019 } -@incollection{cgal:ovja-pssd-15b -, author = "Sven Oesau and Yannick Verdie and Cl{\'e}ment Jamin and Pierre Alliez" -, title = "Point Set Shape Detection" +@incollection{cgal:ovja-pssd-19b +, author = "Sven Oesau and Yannick Verdie and Cl{\'e}ment Jamin and Pierre Alliez and Florent Lafarge and Simon Giraudot and Thien Hoang and Dmitry Anisimov" +, title = "Shape Detection" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPointSetShapeDetection3Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgShapeDetection" +, year = 2019 } -@incollection{cgal:m-ps-15b +@incollection{cgal:m-ps-19b , author = "Abdelkrim Mebarki" , title = "{2D} Placement of Streamlines" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgStreamLines2" +, year = 2019 +} + +@incollection{cgal:lm-clscm-12-19b +, author = "Simon Giraudot and Florent Lafarge" +, title = "Classification" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPlacementOfStreamlines2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgClassification" +, year = 2019 } -@incollection{cgal:b-ss2-15b +@incollection{cgal:cvf-hm3-19b +, author = "Keenan Crane and Christina Vaz and Andreas Fabri" +, title = "The Heat Method" +, publisher = "{CGAL Editorial Board}" +, edition = "{5.0}" +, booktitle = "{CGAL} User and Reference Manual" +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgHeatMethodSummary" +, year = 2019 +} + +@incollection{cgal:b-ss2-19b , author = "Matthias B{\"a}sken" , title = "{2D} Range and Neighbor Search" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPointSet2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPointSet2" +, year = 2019 } -@incollection{cgal:f-isl-15b +@incollection{cgal:f-isl-19b , author = "Andreas Fabri" , title = "Interval Skip List" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgIntervalSkipListSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgIntervalSkipList" +, year = 2019 } -@incollection{cgal:tf-ssd-15b +@incollection{cgal:tf-ssd-19b , author = "Hans Tangelder and Andreas Fabri" , title = "{dD} Spatial Searching" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSpatialSearchingDSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSpatialSearchingD" +, year = 2019 } -@incollection{cgal:n-rstd-15b +@incollection{cgal:n-rstd-19b , author = "Gabriele Neyer" , title = "{dD} Range and Segment Trees" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgRangeSegmentTreesDSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSearchStructures" +, year = 2019 } -@incollection{cgal:kmz-isiobd-15b +@incollection{cgal:kmz-isiobd-19b , author = "Lutz Kettner and Andreas Meyer and Afra Zomorodian" , title = "Intersecting Sequences of {dD} Iso-oriented Boxes" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgBoxIntersectionDSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgBoxIntersectionD" +, year = 2019 } -@incollection{cgal:atw-aabb-15b +@incollection{cgal:atw-aabb-19b , author = "Pierre Alliez and St{\'e}phane Tayeb and Camille Wormser" , title = "{3D} Fast Intersection and Distance Computation" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgAABB_treeSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgAABBTree" +, year = 2019 } -@incollection{cgal:dd-ss-15b +@incollection{cgal:dd-ss-19b , author = "Christophe Delage and Olivier Devillers" , title = "Spatial Sorting" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgSpatialSortingSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSpatialSorting" +, year = 2019 } -@incollection{cgal:fghhs-bv-15b +@incollection{cgal:fghhs-bv-19b , author = "Kaspar Fischer and Bernd G{\"a}rtner and Thomas Herrmann and Michael Hoffmann and Sven Sch{\"o}nherr" , title = "Bounding Volumes" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgBoundingVolumesSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgBoundingVolumes" +, year = 2019 } -@incollection{cgal:hp-ia-15b +@incollection{cgal:hp-ia-19b , author = "Michael Hoffmann and Eli Packer" , title = "Inscribed Areas" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgInscribedAreasSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgInscribedAreas" +, year = 2019 } -@incollection{cgal:fghhs-od-15b +@incollection{cgal:fghhs-od-19b , author = "Kaspar Fischer and Bernd G{\"a}rtner and Thomas Herrmann and Michael Hoffmann and Sven Sch{\"o}nherr" , title = "Optimal Distances" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgOptimalDistancesSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPolytopeDistanceD" +, year = 2019 } -@incollection{cgal:ap-pcad-15b +@incollection{cgal:ap-pcad-19b , author = "Pierre Alliez and Sylvain Pion and Ankit Gupta" , title = "Principal Component Analysis" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgPrincipalComponentAnalysisDSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPrincipalComponentAnalysisD" +, year = 2019 } -@incollection{cgal:f-i-15b +@incollection{cgal:f-i-19b , author = "Julia Fl{\"o}totto" , title = "{2D} and Surface Function Interpolation" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgInterpolation2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgInterpolation2" +, year = 2019 } -@incollection{cgal:abha-gbc-15b +@incollection{cgal:abha-gbc-19b , author = "Dmitry Anisimov and David Bommes and Kai Hormann and Pierre Alliez" , title = "{2D} Generalized Barycentric Coordinates" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgBarycentric_coordinates_2Summary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgBarycentricCoordinates2" +, year = 2019 } -@incollection{cgal:r-kds-15b -, author = "Daniel Russel" -, title = "Kinetic Data Structures" +@incollection{cgal:hkpw-se-19b +, author = "Michael Hoffmann and Lutz Kettner and Sylvain Pion and Ron Wein" +, title = "STL Extensions for {CGAL}" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgKdsSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSTLExtension" +, year = 2019 } -@incollection{cgal:r-kdsf-15b -, author = "Daniel Russel" -, title = "Kinetic Framework" +@incollection{cgal:cfw-cbgl-19b +, author = "Andreas Fabri and Fernando Cacciola and Philipp Moeller and Ron Wein" +, title = "{CGAL} and the {Boost} Graph Library" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgKdsFrameworkSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgBGL" +, year = 2019 } -@incollection{cgal:hkpw-se-15b -, author = "Michael Hoffmann and Lutz Kettner and Sylvain Pion and Ron Wein" -, title = "STL Extensions for {CGAL}" +@incollection{cgal:eb-solver-19b +, author = "Simon Giraudot and Pierre Alliez and Fr{\'e}d{\'e}ric Cazals and Ga{\"e}l Guennebaud and Bruno L{\'e}vy and Marc Pouget and Laurent Saboret and Liangliang Nan" +, title = "{CGAL} and Solvers" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgStlExtensionSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgSolverInterface" +, year = 2019 } -@incollection{cgal:cfw-cbgl-15b -, author = "Andreas Fabri and Fernando Cacciola and Philipp Moeller and Ron Wein" -, title = "{CGAL} and the {Boost} Graph Library" +@incollection{cgal:fs-cbpm-19b +, author = "Andreas Fabri and Laurent Saboret" +, title = "{CGAL} and {Boost} Property Maps" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgBGLSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgPropertyMap" +, year = 2019 } -@incollection{cgal:fs-cbpm-15b -, author = "Andreas Fabri and Laurent Saboret" -, title = "{CGAL} and {Boost} Property Maps" +@incollection{cgal:st-cbs-19b +, author = "Weisheng Si and Quincy Tse and Fr{\'e}d{\'e}rik Paradis" +, title = "Cone-Based Spanners" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgProperty_mapSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgConeSpanners2" +, year = 2019 } -@incollection{cgal:dksy-hc-15b +@incollection{cgal:dksy-hc-19b , author = "Olivier Devillers and Lutz Kettner and Sylvain Pion and Michael Seel and Mariette Yvinec" , title = "Handles and Circulators" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgHandlesAndCirculatorsSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgHandlesAndCirculators" +, year = 2019 } -@incollection{cgal:dhhk-gog-15b -, author = "Pedro M. M. de Castro and Olivier Devillers and Susan Hert and Michael Hoffmann and Lutz Kettner and Sven Sch{\"o}nherr and Alexandru Tifrea" +@incollection{cgal:dhhk-gog-19b +, author = "Pedro M. M. de Castro and Olivier Devillers and Susan Hert and Michael Hoffmann and Lutz Kettner and Sven Sch{\"o}nherr and Alexandru Tifrea and Maxime Gimeno" , title = "Geometric Object Generators" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgGeneratorsSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgGenerators" +, year = 2019 } -@incollection{cgal:kps-pthum-15b +@incollection{cgal:kps-pthum-19b , author = "Lutz Kettner and Sylvain Pion and Michael Seel" , title = "Profiling tools, Hash Map, Union-find, Modifiers" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgProfilingToolsSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#Miscellany" +, year = 2019 } -@incollection{cgal:fgk-ios-12-15b +@incollection{cgal:fgk-ios-12-19b , author = "Andreas Fabri and Geert-Jan Giezeman and Lutz Kettner" , title = "IO Streams" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgIOstreamsSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgStreamSupport" +, year = 2019 } -@incollection{cgal:fp-gv-15b +@incollection{cgal:fp-gv-19b , author = "Andreas Fabri and Sylvain Pion" , title = "Geomview" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgGeomviewSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgGeomview" +, year = 2019 } -@incollection{cgal:fr-cqgvf-15b +@incollection{cgal:fr-cqgvf-19b , author = "Andreas Fabri and Laurent Rineau" , title = "{CGAL} and the {Qt} Graphics View Framework" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgGraphicsViewSummary" -, year = 2015 +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgGraphicsView" +, year = 2019 } -@incollection{cgal:lp-gi-15b +@incollection{cgal:lp-gi-19b , author = "Olivier Devillers and S{\'e}bastien Loriot and Sylvain Pion" , title = "{CGAL} Ipelets" , publisher = "{CGAL Editorial Board}" -, edition = "{4.7}" +, edition = "{5.0}" , booktitle = "{CGAL} User and Reference Manual" -, url = "http://doc.cgal.org/4.7/Manual/packages.html#PkgCGALIpeletsSummary" -, year = 2015 -} +, url = "https://doc.cgal.org/5.0/Manual/packages.html#PkgCGALIpelets" +, year = 2019 +} \ No newline at end of file -- cgit v1.2.3 From 9f56ba3f8df7539109edb3d065124f21c10a51ef Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 22 Nov 2019 23:35:12 +0100 Subject: Update gudhi bibtex --- biblio/how_to_cite_gudhi.bib | 80 ++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/biblio/how_to_cite_gudhi.bib b/biblio/how_to_cite_gudhi.bib index 942f8d7e..e61f15c6 100644 --- a/biblio/how_to_cite_gudhi.bib +++ b/biblio/how_to_cite_gudhi.bib @@ -2,141 +2,157 @@ , title = "{GUDHI} User and Reference Manual" , author = "{The GUDHI Project}" , publisher = "{GUDHI Editorial Board}" -, year = 2015 -, url = "http://gudhi.gforge.inria.fr/doc/latest/" +, edition = "{3.0.0}" +, year = 2019 +, url = "https://gudhi.inria.fr/doc/latest/" } @incollection{gudhi:FilteredComplexes , author = "Cl\'ement Maria" , title = "Filtered Complexes" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__simplex__tree.html" -, year = 2015 +, url = "https://gudhi.inria.fr/doc/latest/group__simplex__tree.html" +, year = 2019 } @incollection{gudhi:PersistentCohomology , author = "Cl\'ement Maria" , title = "Persistent Cohomology" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__persistent__cohomology.html" -, year = 2015 +, url = "https://gudhi.inria.fr/doc/latest/group__persistent__cohomology.html" +, year = 2019 } @incollection{gudhi:Contraction , author = "David Salinas" , title = "Contraction" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__contr.html" -, year = 2015 +, url = "https://gudhi.inria.fr/doc/latest/group__contr.html" +, year = 2019 } @incollection{gudhi:SkeletonBlocker , author = "David Salinas" , title = "Skeleton-Blocker" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__skbl.html" -, year = 2015 +, url = "https://gudhi.inria.fr/doc/latest/group__skbl.html" +, year = 2019 } @incollection{gudhi:AlphaComplex , author = "Vincent Rouvreau" , title = "Alpha complex" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__alpha__complex.html" -, year = 2015 +, url = "https://gudhi.inria.fr/doc/latest/group__alpha__complex.html" +, year = 2019 } @incollection{gudhi:CubicalComplex , author = "Pawel Dlotko" , title = "Cubical complex" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__cubical__complex.html" -, year = 2015 +, url = "https://gudhi.inria.fr/doc/latest/group__cubical__complex.html" +, year = 2019 } @incollection{gudhi:WitnessComplex , author = "Siargey Kachanovich" , title = "Witness complex" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__witness__complex.html" -, year = 2015 +, url = "https://gudhi.inria.fr/doc/latest/group__witness__complex.html" +, year = 2019 } @incollection{gudhi:SubSampling , author = "Cl\'ement Jamin, Siargey Kachanovich" , title = "Subsampling" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__subsampling.html" -, year = 2016 +, url = "https://gudhi.inria.fr/doc/latest/group__subsampling.html" +, year = 2019 } @incollection{gudhi:SpatialSearching , author = "Cl\'ement Jamin" , title = "Spatial searching" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__spatial__searching.html" -, year = 2016 +, url = "https://gudhi.inria.fr/doc/latest/group__spatial__searching.html" +, year = 2019 } @incollection{gudhi:TangentialComplex , author = "Cl\'ement Jamin" , title = "Tangential complex" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__tangential__complex.html" -, year = 2016 +, url = "https://gudhi.inria.fr/doc/latest/group__tangential__complex.html" +, year = 2019 } @incollection{gudhi:RipsComplex , author = "Cl\'ement Maria, Pawel Dlotko, Vincent Rouvreau, Marc Glisse" , title = "Rips complex" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__rips__complex.html" -, year = 2016 +, url = "https://gudhi.inria.fr/doc/latest/group__rips__complex.html" +, year = 2019 } @incollection{gudhi:BottleneckDistance , author = "Fran{{\c{c}}ois Godi" , title = "Bottleneck distance" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__bottleneck__distance.html" -, year = 2016 +, url = "https://gudhi.inria.fr/doc/latest/group__bottleneck__distance.html" +, year = 2019 } @incollection{gudhi:cython , author = "Vincent Rouvreau" , title = "Cython interface" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/python/latest/" -, year = 2016 +, url = "https://gudhi.inria.fr/python/latest/" +, year = 2019 } @incollection{gudhi:CoverComplex , author = "Mathieu Carri\`ere" , title = "Cover complex" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group__cover__complex.html" -, year = 2017 +, url = "https://gudhi.inria.fr/doc/latest/group__cover__complex.html" +, year = 2019 } @incollection{gudhi:PersistenceRepresentations , author = "Pawel Dlotko" , title = "Persistence representations" , publisher = "{GUDHI Editorial Board}" +, edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "http://gudhi.gforge.inria.fr/doc/latest/group___persistence__representations.html" -, year = 2017 +, url = "https://gudhi.inria.fr/doc/latest/group___persistence__representations.html" +, year = 2019 } -- cgit v1.2.3 From 3b2994998d1e140005cc9155cafc0b0065e8cab1 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 26 Nov 2019 11:23:56 +0100 Subject: Citing shall refer to a specific version, not latest --- biblio/how_to_cite_gudhi.bib | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/biblio/how_to_cite_gudhi.bib b/biblio/how_to_cite_gudhi.bib index e61f15c6..cba94d4d 100644 --- a/biblio/how_to_cite_gudhi.bib +++ b/biblio/how_to_cite_gudhi.bib @@ -4,7 +4,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , year = 2019 -, url = "https://gudhi.inria.fr/doc/latest/" +, url = "https://gudhi.inria.fr/doc/3.0.0/" } @incollection{gudhi:FilteredComplexes @@ -13,7 +13,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__simplex__tree.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__simplex__tree.html" , year = 2019 } @@ -23,7 +23,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__persistent__cohomology.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__persistent__cohomology.html" , year = 2019 } @@ -33,7 +33,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__contr.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__contr.html" , year = 2019 } @@ -43,7 +43,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__skbl.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__skbl.html" , year = 2019 } @@ -53,7 +53,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__alpha__complex.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__alpha__complex.html" , year = 2019 } @@ -63,7 +63,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__cubical__complex.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__cubical__complex.html" , year = 2019 } @@ -73,7 +73,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__witness__complex.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__witness__complex.html" , year = 2019 } @@ -83,7 +83,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__subsampling.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__subsampling.html" , year = 2019 } @@ -93,7 +93,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__spatial__searching.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__spatial__searching.html" , year = 2019 } @@ -103,7 +103,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__tangential__complex.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__tangential__complex.html" , year = 2019 } @@ -113,7 +113,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__rips__complex.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__rips__complex.html" , year = 2019 } @@ -123,7 +123,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__bottleneck__distance.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__bottleneck__distance.html" , year = 2019 } @@ -133,7 +133,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/python/latest/" +, url = "https://gudhi.inria.fr/python/3.0.0/" , year = 2019 } @@ -143,7 +143,7 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group__cover__complex.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group__cover__complex.html" , year = 2019 } @@ -153,6 +153,6 @@ , publisher = "{GUDHI Editorial Board}" , edition = "{3.0.0}" , booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/latest/group___persistence__representations.html" +, url = "https://gudhi.inria.fr/doc/3.0.0/group___persistence__representations.html" , year = 2019 } -- cgit v1.2.3 From 1caf1fb2ea7b12ddb12004758ebf4d28871ec7cd Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 26 Nov 2019 11:30:58 +0100 Subject: Fix doxygen warnings about new cgal bibtex entries --- src/Alpha_complex/doc/Intro_alpha_complex.h | 6 +++--- src/Alpha_complex/include/gudhi/Alpha_complex.h | 2 +- src/common/doc/installation.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Alpha_complex/doc/Intro_alpha_complex.h b/src/Alpha_complex/doc/Intro_alpha_complex.h index adc1378f..3c32a1e6 100644 --- a/src/Alpha_complex/doc/Intro_alpha_complex.h +++ b/src/Alpha_complex/doc/Intro_alpha_complex.h @@ -38,12 +38,12 @@ namespace alpha_complex { * * Alpha_complex is constructing a Delaunay Triangulation - * \cite cgal:hdj-t-15b from CGAL (the Computational Geometry - * Algorithms Library \cite cgal:eb-15b) and is able to create a `SimplicialComplexForAlpha`. + * \cite cgal:hdj-t-19b from CGAL (the Computational Geometry + * Algorithms Library \cite cgal:eb-19b) and is able to create a `SimplicialComplexForAlpha`. * * The complex is a template class requiring an Epick_d dD Geometry Kernel - * \cite cgal:s-gkd-15b from CGAL as template parameter. + * \cite cgal:s-gkd-19b from CGAL as template parameter. * * \remark * - When the simplicial complex is constructed with an infinite value of alpha, the complex is a Delaunay diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 0c2569c8..6b4d8463 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -72,7 +72,7 @@ template struct Is_Epeck_D> { static const bool val * or an CGAL::Epick_d dD Geometry Kernel - * \cite cgal:s-gkd-15b from CGAL as template, default value is CGAL::Epeck_d * < * CGAL::Dynamic_dimension_tag > diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index 2e64bef8..c320e7e0 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -71,7 +71,7 @@ make doxygen * your operating system is detailed here http://doc.cgal.org/latest/Manual/installation.html * * The following examples/utilities require the Computational Geometry Algorithms - * Library (CGAL \cite cgal:eb-15b) and will not be built if CGAL version 4.11.0 or higher is not installed: + * Library (CGAL \cite cgal:eb-19b) and will not be built if CGAL version 4.11.0 or higher is not installed: * \li * Simplex_tree/example_alpha_shapes_3_simplex_tree_from_off_file.cpp * \li -- cgit v1.2.3 From 177e80b653d60119acb4455feaba02615083532b Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 26 Nov 2019 17:51:50 +0100 Subject: Fix link. --- src/python/doc/representations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index a137a035..c870f834 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -10,7 +10,7 @@ Representations manual This module, originally named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning tools, in particular scikit-learn. It provides tools, using the scikit-learn standard interface, to compute distances and kernels on diagrams, and to convert diagrams into vectors. -A diagram is represented as a numpy array of shape (n,2), as can be obtained from `SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. +A diagram is represented as a numpy array of shape (n,2), as can be obtained from :func:`gudhi.SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. A small example is provided -- cgit v1.2.3 From 1e1cb09adf07f0351557d3bf8011c9fc3e1b4628 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 27 Nov 2019 10:31:23 +0100 Subject: Fix #150 axes has no attribute colorbar --- src/python/gudhi/persistence_graphical_tools.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/python/gudhi/persistence_graphical_tools.py b/src/python/gudhi/persistence_graphical_tools.py index c9dab323..7d232c85 100644 --- a/src/python/gudhi/persistence_graphical_tools.py +++ b/src/python/gudhi/persistence_graphical_tools.py @@ -369,7 +369,6 @@ def plot_persistence_density( persistence_dim = read_persistence_intervals_in_dimension( persistence_file=persistence_file, only_this_dim=dimension ) - print(persistence_dim) else: print("file " + persistence_file + " not found.") return None @@ -417,10 +416,10 @@ def plot_persistence_density( zi = k(np.vstack([xi.flatten(), yi.flatten()])) # Make the plot - axes.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=cmap) + img = axes.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=cmap) if legend: - axes.colorbar() + plt.colorbar(img, ax=axes) axes.set_xlabel("Birth") axes.set_ylabel("Death") -- cgit v1.2.3 From ae13d5bbcd83baa9b814b061c04b50c5c31bfc78 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 27 Nov 2019 14:01:56 +0100 Subject: Fix #112 density side by side with its diagram --- src/python/doc/persistence_graphical_tools_user.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/python/doc/persistence_graphical_tools_user.rst b/src/python/doc/persistence_graphical_tools_user.rst index 2de99252..e4419bc1 100644 --- a/src/python/doc/persistence_graphical_tools_user.rst +++ b/src/python/doc/persistence_graphical_tools_user.rst @@ -67,10 +67,17 @@ If you want more information on a specific dimension, for instance: import matplotlib.pyplot as plot import gudhi - # rips_on_tore3D_1307.pers obtained from write_persistence_diagram method persistence_file=gudhi.__root_source_dir__ + \ '/data/persistence_diagram/rips_on_tore3D_1307.pers' - gudhi.plot_persistence_density(persistence_file=persistence_file, - max_intervals=0, dimension=1, legend=True) + birth_death = gudhi.read_persistence_intervals_in_dimension( + persistence_file=persistence_file, + only_this_dim=1) + pers_diag = [(1, (elt[0], elt[1])) for elt in birth_death] + # Use subplots to display diagram and density side by side + fig, axes = plot.subplots(nrows=1, ncols=2, figsize=(12, 5)) + gudhi.plot_persistence_diagram(persistence=pers_diag, + axes=axes[0]) + gudhi.plot_persistence_density(persistence=pers_diag, + dimension=1, legend=True, axes=axes[1]) plot.show() -- cgit v1.2.3 From dbb5183230e4b16bd36c5d582ef82e880741f149 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 27 Nov 2019 21:53:31 +0100 Subject: Code review : for loop simplification --- src/python/doc/persistence_graphical_tools_user.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/doc/persistence_graphical_tools_user.rst b/src/python/doc/persistence_graphical_tools_user.rst index e4419bc1..f41a926b 100644 --- a/src/python/doc/persistence_graphical_tools_user.rst +++ b/src/python/doc/persistence_graphical_tools_user.rst @@ -73,7 +73,7 @@ If you want more information on a specific dimension, for instance: birth_death = gudhi.read_persistence_intervals_in_dimension( persistence_file=persistence_file, only_this_dim=1) - pers_diag = [(1, (elt[0], elt[1])) for elt in birth_death] + pers_diag = [(1, elt) for elt in birth_death] # Use subplots to display diagram and density side by side fig, axes = plot.subplots(nrows=1, ncols=2, figsize=(12, 5)) gudhi.plot_persistence_diagram(persistence=pers_diag, -- cgit v1.2.3 From 8ebfb8c5de9c55a20e3dafebc8f506ccb698bb68 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 26 Nov 2019 23:28:40 +0100 Subject: Add sphinx paramlinks to CI --- .travis.yml | 2 +- Dockerfile_for_circleci_image | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 27514ef5..fd87b083 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,7 +55,7 @@ before_cache: # When installing through libcgal-dev apt, CMake Error at CGAL Exports.cmake The imported target "CGAL::CGAL Qt5" references the file install: - python3 -m pip install --upgrade pip setuptools wheel - - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex matplotlib numpy scipy + - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex sphinx-paramlinks matplotlib numpy scipy - python3 -m pip install --user POT script: diff --git a/Dockerfile_for_circleci_image b/Dockerfile_for_circleci_image index 0dd408fc..f0c73d76 100644 --- a/Dockerfile_for_circleci_image +++ b/Dockerfile_for_circleci_image @@ -54,6 +54,7 @@ RUN pip3 install \ POT \ scikit-learn \ sphinx \ + sphinx-paramlinks \ sphinxcontrib-bibtex # apt clean up -- cgit v1.2.3 From fe3bbb9b3de5001ba943d3be7109712847ec44ef Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Tue, 26 Nov 2019 23:08:53 +0100 Subject: Fix various links for sphinx and some minor doc changes along the way. (why were we documenting a hasse diagram that doesn't exist?) --- src/python/doc/alpha_complex_user.rst | 15 ++++++++------- src/python/doc/conf.py | 1 + src/python/doc/representations.rst | 2 +- src/python/doc/rips_complex_user.rst | 23 ++++++++++++----------- src/python/doc/simplex_tree_user.rst | 10 +++------- src/python/doc/tangential_complex_user.rst | 6 +++--- src/python/doc/witness_complex_user.rst | 4 ++-- src/python/gudhi/tangential_complex.pyx | 4 ++-- 8 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/python/doc/alpha_complex_user.rst b/src/python/doc/alpha_complex_user.rst index f9662a6d..950db3cd 100644 --- a/src/python/doc/alpha_complex_user.rst +++ b/src/python/doc/alpha_complex_user.rst @@ -9,19 +9,19 @@ Definition .. include:: alpha_complex_sum.inc -Alpha_complex is constructing a :doc:`Simplex_tree ` using +`AlphaComplex` is constructing a :doc:`SimplexTree ` using `Delaunay Triangulation `_ :cite:`cgal:hdj-t-15b` from `CGAL `_ (the Computational Geometry Algorithms Library :cite:`cgal:eb-15b`). Remarks ^^^^^^^ -When Alpha_complex is constructed with an infinite value of :math:`\alpha`, the complex is a Delaunay complex. +When an :math:`\alpha`-complex is constructed with an infinite value of :math:`\alpha`, the complex is a Delaunay complex (with special filtration values). Example from points ------------------- -This example builds the Delaunay triangulation from the given points, and initializes the alpha complex with it: +This example builds the alpha-complex from the given points: .. testcode:: @@ -139,15 +139,16 @@ Non decreasing filtration values As the squared radii computed by CGAL are an approximation, it might happen that these alpha squared values do not quite define a proper filtration (i.e. non-decreasing with respect to inclusion). -We fix that up by calling `Simplex_tree::make_filtration_non_decreasing()` (cf. +We fix that up by calling :func:`~gudhi.SimplexTree.make_filtration_non_decreasing` (cf. `C++ version `_). Prune above given filtration value ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The simplex tree is pruned from the given maximum alpha squared value (cf. `Simplex_tree::prune_above_filtration()` -in the `C++ version `_). Note that this does not provide any kind -of speed-up, since we always first build the full filtered complex, so it is recommended not to use `max_alpha_square`. +The simplex tree is pruned from the given maximum alpha squared value (cf. +:func:`~gudhi.SimplexTree.prune_above_filtration`). Note that this does not provide any kind +of speed-up, since we always first build the full filtered complex, so it is recommended not to use +:paramref:`~gudhi.AlphaComplex.create_simplex_tree.max_alpha_square`. In the following example, a threshold of 59 is used. diff --git a/src/python/doc/conf.py b/src/python/doc/conf.py index 64d9cba1..3cc5d1d6 100755 --- a/src/python/doc/conf.py +++ b/src/python/doc/conf.py @@ -41,6 +41,7 @@ extensions = [ 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'sphinxcontrib.bibtex', + 'sphinx_paramlinks', ] todo_include_todos = True diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index c870f834..b3131a25 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -10,7 +10,7 @@ Representations manual This module, originally named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning tools, in particular scikit-learn. It provides tools, using the scikit-learn standard interface, to compute distances and kernels on diagrams, and to convert diagrams into vectors. -A diagram is represented as a numpy array of shape (n,2), as can be obtained from :func:`gudhi.SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. +A diagram is represented as a numpy array of shape (n,2), as can be obtained from :func:`~gudhi.SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. A small example is provided diff --git a/src/python/doc/rips_complex_user.rst b/src/python/doc/rips_complex_user.rst index 3f6b960d..a8659542 100644 --- a/src/python/doc/rips_complex_user.rst +++ b/src/python/doc/rips_complex_user.rst @@ -40,12 +40,12 @@ A vertex name corresponds to the index of the point in the given range (aka. the On this example, as edges (4,5), (4,6) and (5,6) are in the complex, simplex (4,5,6) is added with the filtration value set with :math:`max(filtration(4,5), filtration(4,6), filtration(5,6))`. And so on for simplex (0,1,2,3). -If the `RipsComplex` interfaces are not detailed enough for your need, please refer to rips_persistence_step_by_step.cpp -C++ example, where the graph construction over the Simplex_tree is more detailed. +If the :doc:`RipsComplex ` interfaces are not detailed enough for your need, please refer to +rips_persistence_step_by_step.cpp C++ example, where the graph construction over the Simplex_tree is more detailed. A Rips complex can easily become huge, even if we limit the length of the edges and the dimension of the simplices. One easy trick, before building a Rips -complex on a point cloud, is to call `sparsify_point_set` which removes points +complex on a point cloud, is to call :func:`~gudhi.sparsify_point_set` which removes points that are too close to each other. This does not change its persistence diagram by more than the length used to define "too close". @@ -57,7 +57,7 @@ a :math:`\frac{1}{1-\varepsilon}`-interleaving, although in practice the error is usually smaller. A more intuitive presentation of the idea is available in :cite:`cavanna15geometric`, and in a video :cite:`cavanna15visualizing`. Passing an extra argument `sparse=0.3` at the -construction of a `RipsComplex` object asks it to build a sparse Rips with +construction of a :class:`~gudhi.RipsComplex` object asks it to build a sparse Rips with parameter :math:`\varepsilon=0.3`, while the default `sparse=None` builds the regular Rips complex. @@ -69,7 +69,7 @@ Example from a point cloud ^^^^^^^^^^^^^^^^^^^^^^^^^^ This example builds the neighborhood graph from the given points, up to max_edge_length. -Then it creates a :doc:`Simplex_tree ` with it. +Then it creates a :doc:`SimplexTree ` with it. Finally, it is asked to display information about the simplicial complex. @@ -128,7 +128,7 @@ Example from OFF file This example builds the :doc:`RipsComplex ` from the given points in an OFF file, and max_edge_length value. -Then it creates a :doc:`Simplex_tree ` with it. +Then it creates a :doc:`SimplexTree ` with it. Finally, it is asked to display information about the Rips complex. @@ -178,7 +178,7 @@ Example from a distance matrix ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This example builds the one skeleton graph from the given distance matrix, and max_edge_length value. -Then it creates a :doc:`Simplex_tree ` with it. +Then it creates a :doc:`SimplexTree ` with it. Finally, it is asked to display information about the simplicial complex. @@ -233,7 +233,7 @@ Example from csv file This example builds the :doc:`RipsComplex ` from the given distance matrix in a csv file, and max_edge_length value. -Then it creates a :doc:`Simplex_tree ` with it. +Then it creates a :doc:`SimplexTree ` with it. Finally, it is asked to display information about the Rips complex. @@ -286,7 +286,7 @@ Example from a correlation matrix Analogously to the case of distance matrix, Rips complexes can be also constructed based on correlation matrix. Given a correlation matrix M, comportment-wise 1-M is a distance matrix. This example builds the one skeleton graph from the given corelation matrix and threshold value. -Then it creates a :doc:`Simplex_tree ` with it. +Then it creates a :doc:`SimplexTree ` with it. Finally, it is asked to display information about the simplicial complex. @@ -307,7 +307,7 @@ Finally, it is asked to display information about the simplicial complex. [0.01, 0.01, 0.72, 1., 0.7], [0.89, 0.61, 0.03, 0.7, 1.]], float) - distance_matrix = np.ones((correlation_matrix.shape),float) - correlation_matrix + distance_matrix = 1 - correlation_matrix rips_complex = gudhi.RipsComplex(distance_matrix=distance_matrix, max_edge_length=1.0) simplex_tree = rips_complex.create_simplex_tree(max_dimension=1) @@ -342,6 +342,7 @@ until dimension 1 - one skeleton graph in other words), the output is: [1, 3] -> 0.99 .. note:: - As persistence diagrams points will be under the diagonal, + If you compute the persistence diagram and convert distances back to correlation values, + points in the persistence diagram will be under the diagonal, and bottleneck distance and persistence graphical tool will not work properly, this is a known issue. diff --git a/src/python/doc/simplex_tree_user.rst b/src/python/doc/simplex_tree_user.rst index aebeb29f..3df7617f 100644 --- a/src/python/doc/simplex_tree_user.rst +++ b/src/python/doc/simplex_tree_user.rst @@ -23,13 +23,9 @@ scheme. Implementation -------------- -There are two implementation of complexes. The first on is the Simplex_tree data structure. -The simplex tree is an efficient and flexible data structure for representing general (filtered) simplicial complexes. -The data structure is described in :cite`boissonnatmariasimplextreealgorithmica`. - -The second one is the Hasse_complex. The Hasse complex is a data structure representing explicitly all co-dimension 1 -incidence relations in a complex. It is consequently faster when accessing the boundary of a simplex, but is less -compact and harder to construct from scratch. +The :class:`simplex tree` is an efficient and flexible data structure for representing general +(filtered) simplicial complexes. +The data structure is described in :cite:`boissonnatmariasimplextreealgorithmica`. Example ------- diff --git a/src/python/doc/tangential_complex_user.rst b/src/python/doc/tangential_complex_user.rst index ebfe1e29..852cf5b6 100644 --- a/src/python/doc/tangential_complex_user.rst +++ b/src/python/doc/tangential_complex_user.rst @@ -107,12 +107,12 @@ inconsistencies, but is not guaranteed to succeed. Output ^^^^^^ -The result of the computation is exported as a Simplex_tree. It is the union of +The result of the computation is exported as a :class:`~gudhi.SimplexTree`. It is the union of the stars of all the input points. A vertex in the Simplex Tree is the index of the point in the range provided by the user. The point corresponding to a -vertex can also be obtained through the Tangential_complex::get_point function. +vertex can also be obtained through the :func:`gudhi.TangentialComplex.get_point` function. Note that even if the positions of the points are perturbed, their original -positions are kept (e.g. Tangential_complex::get_point returns the original +positions are kept (e.g. :func:`~gudhi.TangentialComplex.get_point` returns the original position of the point). The result can be obtained after the computation of the Tangential complex diff --git a/src/python/doc/witness_complex_user.rst b/src/python/doc/witness_complex_user.rst index 40e94134..45ba5b3b 100644 --- a/src/python/doc/witness_complex_user.rst +++ b/src/python/doc/witness_complex_user.rst @@ -47,7 +47,7 @@ which leads to definitions of **weak relaxed witness complex** (or just relaxed In particular case of 0-relaxation, weak complex corresponds to **witness complex** introduced in :cite:`de2004topological`, whereas 0-relaxed strong witness complex consists of just vertices and is not very interesting. Hence for small relaxation weak version is preferable. -However, to capture the homotopy type (for example using Gudhi::persistent_cohomology::Persistent_cohomology) it is +However, to capture the homotopy type (for example using :func:`gudhi.SimplexTree.persistence`) it is often necessary to work with higher filtration values. In this case strong relaxed witness complex is faster to compute and offers similar results. @@ -69,7 +69,7 @@ The construction of the Euclidean versions of complexes follow the same scheme: In the non-Euclidean classes, the lists of nearest landmarks are supposed to be given as input. -The constructors take on the steps 1 and 2, while the function 'create_complex' executes the step 3. +The constructors take on the steps 1 and 2, while the function :func:`!create_complex` executes the step 3. Constructing weak relaxed witness complex from an off file ---------------------------------------------------------- diff --git a/src/python/gudhi/tangential_complex.pyx b/src/python/gudhi/tangential_complex.pyx index b7678f4d..f4c8b079 100644 --- a/src/python/gudhi/tangential_complex.pyx +++ b/src/python/gudhi/tangential_complex.pyx @@ -91,7 +91,7 @@ cdef class TangentialComplex: Raises: ValueError: In debug mode, if the computed star dimension is too low. Try to set a bigger maximal edge length value with - :func:`~gudhi.Tangential_complex.set_max_squared_edge_length` + :meth:`set_max_squared_edge_length` if this happens. """ self.thisptr.compute_tangential_complex() @@ -166,7 +166,7 @@ cdef class TangentialComplex: :type max_squared_edge_length: double If the maximal edge length value is too low - :func:`~gudhi.Tangential_complex.compute_tangential_complex` + :meth:`compute_tangential_complex` will throw an exception in debug mode. """ self.thisptr.set_max_squared_edge_length(max_squared_edge_length) -- cgit v1.2.3 From e1404be9da9cda8e6f41e7cf207934f1914a3505 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 2 Dec 2019 17:35:09 +0100 Subject: CMake generates how_to_cite_gudhi.bib to auto update version --- biblio/how_to_cite_gudhi.bib | 158 ---------------------- biblio/how_to_cite_gudhi.bib.in | 158 ++++++++++++++++++++++ src/cmake/modules/GUDHI_user_version_target.cmake | 13 +- 3 files changed, 167 insertions(+), 162 deletions(-) delete mode 100644 biblio/how_to_cite_gudhi.bib create mode 100644 biblio/how_to_cite_gudhi.bib.in diff --git a/biblio/how_to_cite_gudhi.bib b/biblio/how_to_cite_gudhi.bib deleted file mode 100644 index cba94d4d..00000000 --- a/biblio/how_to_cite_gudhi.bib +++ /dev/null @@ -1,158 +0,0 @@ -@book{gudhi:urm -, title = "{GUDHI} User and Reference Manual" -, author = "{The GUDHI Project}" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, year = 2019 -, url = "https://gudhi.inria.fr/doc/3.0.0/" -} - -@incollection{gudhi:FilteredComplexes -, author = "Cl\'ement Maria" -, title = "Filtered Complexes" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__simplex__tree.html" -, year = 2019 -} - -@incollection{gudhi:PersistentCohomology -, author = "Cl\'ement Maria" -, title = "Persistent Cohomology" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__persistent__cohomology.html" -, year = 2019 -} - -@incollection{gudhi:Contraction -, author = "David Salinas" -, title = "Contraction" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__contr.html" -, year = 2019 -} - -@incollection{gudhi:SkeletonBlocker -, author = "David Salinas" -, title = "Skeleton-Blocker" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__skbl.html" -, year = 2019 -} - -@incollection{gudhi:AlphaComplex -, author = "Vincent Rouvreau" -, title = "Alpha complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__alpha__complex.html" -, year = 2019 -} - -@incollection{gudhi:CubicalComplex -, author = "Pawel Dlotko" -, title = "Cubical complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__cubical__complex.html" -, year = 2019 -} - -@incollection{gudhi:WitnessComplex -, author = "Siargey Kachanovich" -, title = "Witness complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__witness__complex.html" -, year = 2019 -} - -@incollection{gudhi:SubSampling -, author = "Cl\'ement Jamin, Siargey Kachanovich" -, title = "Subsampling" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__subsampling.html" -, year = 2019 -} - -@incollection{gudhi:SpatialSearching -, author = "Cl\'ement Jamin" -, title = "Spatial searching" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__spatial__searching.html" -, year = 2019 -} - -@incollection{gudhi:TangentialComplex -, author = "Cl\'ement Jamin" -, title = "Tangential complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__tangential__complex.html" -, year = 2019 -} - -@incollection{gudhi:RipsComplex -, author = "Cl\'ement Maria, Pawel Dlotko, Vincent Rouvreau, Marc Glisse" -, title = "Rips complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__rips__complex.html" -, year = 2019 -} - -@incollection{gudhi:BottleneckDistance -, author = "Fran{{\c{c}}ois Godi" -, title = "Bottleneck distance" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__bottleneck__distance.html" -, year = 2019 -} - -@incollection{gudhi:cython -, author = "Vincent Rouvreau" -, title = "Cython interface" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/python/3.0.0/" -, year = 2019 -} - -@incollection{gudhi:CoverComplex -, author = "Mathieu Carri\`ere" -, title = "Cover complex" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group__cover__complex.html" -, year = 2019 -} - -@incollection{gudhi:PersistenceRepresentations -, author = "Pawel Dlotko" -, title = "Persistence representations" -, publisher = "{GUDHI Editorial Board}" -, edition = "{3.0.0}" -, booktitle = "{GUDHI} User and Reference Manual" -, url = "https://gudhi.inria.fr/doc/3.0.0/group___persistence__representations.html" -, year = 2019 -} diff --git a/biblio/how_to_cite_gudhi.bib.in b/biblio/how_to_cite_gudhi.bib.in new file mode 100644 index 00000000..23a37956 --- /dev/null +++ b/biblio/how_to_cite_gudhi.bib.in @@ -0,0 +1,158 @@ +@book{gudhi:urm +, title = "{GUDHI} User and Reference Manual" +, author = "{The GUDHI Project}" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, year = 2019 +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/" +} + +@incollection{gudhi:FilteredComplexes +, author = "Cl\'ement Maria" +, title = "Filtered Complexes" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__simplex__tree.html" +, year = 2019 +} + +@incollection{gudhi:PersistentCohomology +, author = "Cl\'ement Maria" +, title = "Persistent Cohomology" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__persistent__cohomology.html" +, year = 2019 +} + +@incollection{gudhi:Contraction +, author = "David Salinas" +, title = "Contraction" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__contr.html" +, year = 2019 +} + +@incollection{gudhi:SkeletonBlocker +, author = "David Salinas" +, title = "Skeleton-Blocker" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__skbl.html" +, year = 2019 +} + +@incollection{gudhi:AlphaComplex +, author = "Vincent Rouvreau" +, title = "Alpha complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__alpha__complex.html" +, year = 2019 +} + +@incollection{gudhi:CubicalComplex +, author = "Pawel Dlotko" +, title = "Cubical complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__cubical__complex.html" +, year = 2019 +} + +@incollection{gudhi:WitnessComplex +, author = "Siargey Kachanovich" +, title = "Witness complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__witness__complex.html" +, year = 2019 +} + +@incollection{gudhi:SubSampling +, author = "Cl\'ement Jamin, Siargey Kachanovich" +, title = "Subsampling" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__subsampling.html" +, year = 2019 +} + +@incollection{gudhi:SpatialSearching +, author = "Cl\'ement Jamin" +, title = "Spatial searching" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__spatial__searching.html" +, year = 2019 +} + +@incollection{gudhi:TangentialComplex +, author = "Cl\'ement Jamin" +, title = "Tangential complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__tangential__complex.html" +, year = 2019 +} + +@incollection{gudhi:RipsComplex +, author = "Cl\'ement Maria, Pawel Dlotko, Vincent Rouvreau, Marc Glisse" +, title = "Rips complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__rips__complex.html" +, year = 2019 +} + +@incollection{gudhi:BottleneckDistance +, author = "Fran{{\c{c}}ois Godi" +, title = "Bottleneck distance" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__bottleneck__distance.html" +, year = 2019 +} + +@incollection{gudhi:cython +, author = "Vincent Rouvreau" +, title = "Cython interface" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/python/@GUDHI_VERSION@/" +, year = 2019 +} + +@incollection{gudhi:CoverComplex +, author = "Mathieu Carri\`ere" +, title = "Cover complex" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__cover__complex.html" +, year = 2019 +} + +@incollection{gudhi:PersistenceRepresentations +, author = "Pawel Dlotko" +, title = "Persistence representations" +, publisher = "{GUDHI Editorial Board}" +, edition = "{@GUDHI_VERSION@}" +, booktitle = "{GUDHI} User and Reference Manual" +, url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group___persistence__representations.html" +, year = 2019 +} diff --git a/src/cmake/modules/GUDHI_user_version_target.cmake b/src/cmake/modules/GUDHI_user_version_target.cmake index f75fb19e..c179220c 100644 --- a/src/cmake/modules/GUDHI_user_version_target.cmake +++ b/src/cmake/modules/GUDHI_user_version_target.cmake @@ -18,12 +18,19 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES_FULL_LIST}) set(GUDHI_DOXYGEN_IMAGE_PATH "${GUDHI_DOXYGEN_IMAGE_PATH} doc/${GUDHI_MODULE}/ \\ \n") endforeach(GUDHI_MODULE ${GUDHI_MODULES_FULL_LIST}) -# Generate setup.py file to cythonize Gudhi - This file must be named setup.py by convention +# Generate Doxyfile for Doxygen - cf. root CMakeLists.txt for explanation configure_file(${CMAKE_SOURCE_DIR}/src/Doxyfile.in "${CMAKE_CURRENT_BINARY_DIR}/src/Doxyfile" @ONLY) - add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/src/Doxyfile ${GUDHI_USER_VERSION_DIR}/Doxyfile) +# Generate bib files for Doxygen - cf. root CMakeLists.txt for explanation +configure_file(${CMAKE_SOURCE_DIR}/biblio/how_to_cite_gudhi.bib.in "${CMAKE_CURRENT_BINARY_DIR}/biblio/how_to_cite_gudhi.bib" @ONLY) +file(COPY "${CMAKE_SOURCE_DIR}/biblio/how_to_cite_cgal.bib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/biblio/") +file(COPY "${CMAKE_SOURCE_DIR}/biblio/bibliography.bib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/biblio/") +# Copy biblio directory for user version +add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E + copy_directory ${CMAKE_CURRENT_BINARY_DIR}/biblio ${GUDHI_USER_VERSION_DIR}/biblio) + add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/Conventions.txt ${GUDHI_USER_VERSION_DIR}/Conventions.txt) add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E @@ -39,8 +46,6 @@ add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/CMakeGUDHIVersion.txt ${GUDHI_USER_VERSION_DIR}/CMakeGUDHIVersion.txt) -add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E - copy_directory ${CMAKE_SOURCE_DIR}/biblio ${GUDHI_USER_VERSION_DIR}/biblio) add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/${GUDHI_PYTHON_PATH} ${GUDHI_USER_VERSION_DIR}/python) add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E -- cgit v1.2.3 From 5b01dbc9d59e1e3e5e00d247c6cdeec406a398b5 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 2 Dec 2019 17:53:28 +0100 Subject: Some bad cgal referenced bibliography as it was updated --- src/python/doc/alpha_complex_user.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/doc/alpha_complex_user.rst b/src/python/doc/alpha_complex_user.rst index f9662a6d..dbe19286 100644 --- a/src/python/doc/alpha_complex_user.rst +++ b/src/python/doc/alpha_complex_user.rst @@ -11,8 +11,8 @@ Definition Alpha_complex is constructing a :doc:`Simplex_tree ` using `Delaunay Triangulation `_ -:cite:`cgal:hdj-t-15b` from `CGAL `_ (the Computational Geometry Algorithms Library -:cite:`cgal:eb-15b`). +:cite:`cgal:hdj-t-19b` from `CGAL `_ (the Computational Geometry Algorithms Library +:cite:`cgal:eb-19b`). Remarks ^^^^^^^ -- cgit v1.2.3 From 518de123bcf19a85accca5660844355da1106511 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 2 Dec 2019 23:32:33 +0100 Subject: Code review : year is also generated --- biblio/how_to_cite_gudhi.bib.in | 32 +++++++++++------------ src/cmake/modules/GUDHI_user_version_target.cmake | 1 + 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/biblio/how_to_cite_gudhi.bib.in b/biblio/how_to_cite_gudhi.bib.in index 23a37956..05d3cc98 100644 --- a/biblio/how_to_cite_gudhi.bib.in +++ b/biblio/how_to_cite_gudhi.bib.in @@ -3,7 +3,7 @@ , author = "{The GUDHI Project}" , publisher = "{GUDHI Editorial Board}" , edition = "{@GUDHI_VERSION@}" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/" } @@ -14,7 +14,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__simplex__tree.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:PersistentCohomology @@ -24,7 +24,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__persistent__cohomology.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:Contraction @@ -34,7 +34,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__contr.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:SkeletonBlocker @@ -44,7 +44,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__skbl.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:AlphaComplex @@ -54,7 +54,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__alpha__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:CubicalComplex @@ -64,7 +64,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__cubical__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:WitnessComplex @@ -74,7 +74,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__witness__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:SubSampling @@ -84,7 +84,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__subsampling.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:SpatialSearching @@ -94,7 +94,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__spatial__searching.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:TangentialComplex @@ -104,7 +104,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__tangential__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:RipsComplex @@ -114,7 +114,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__rips__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:BottleneckDistance @@ -124,7 +124,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__bottleneck__distance.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:cython @@ -134,7 +134,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/python/@GUDHI_VERSION@/" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:CoverComplex @@ -144,7 +144,7 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group__cover__complex.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } @incollection{gudhi:PersistenceRepresentations @@ -154,5 +154,5 @@ , edition = "{@GUDHI_VERSION@}" , booktitle = "{GUDHI} User and Reference Manual" , url = "https://gudhi.inria.fr/doc/@GUDHI_VERSION@/group___persistence__representations.html" -, year = 2019 +, year = @GUDHI_VERSION_YEAR@ } diff --git a/src/cmake/modules/GUDHI_user_version_target.cmake b/src/cmake/modules/GUDHI_user_version_target.cmake index c179220c..4fa74330 100644 --- a/src/cmake/modules/GUDHI_user_version_target.cmake +++ b/src/cmake/modules/GUDHI_user_version_target.cmake @@ -24,6 +24,7 @@ add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/src/Doxyfile ${GUDHI_USER_VERSION_DIR}/Doxyfile) # Generate bib files for Doxygen - cf. root CMakeLists.txt for explanation +string(TIMESTAMP GUDHI_VERSION_YEAR "%Y") configure_file(${CMAKE_SOURCE_DIR}/biblio/how_to_cite_gudhi.bib.in "${CMAKE_CURRENT_BINARY_DIR}/biblio/how_to_cite_gudhi.bib" @ONLY) file(COPY "${CMAKE_SOURCE_DIR}/biblio/how_to_cite_cgal.bib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/biblio/") file(COPY "${CMAKE_SOURCE_DIR}/biblio/bibliography.bib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/biblio/") -- cgit v1.2.3 From f87a1026d5d1accbb6a558ef5d50f5c4c035e0d8 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 3 Dec 2019 16:51:09 +0100 Subject: Fix #134 --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 6b4d8463..6f19cb6c 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -121,8 +121,8 @@ class Alpha_complex { // size_type type from CGAL. typedef typename Delaunay_triangulation::size_type size_type; - // Map type to switch from simplex tree vertex handle to CGAL vertex iterator. - typedef typename std::map< std::size_t, CGAL_vertex_iterator > Vector_vertex_iterator; + // Structure to switch from simplex tree vertex handle to CGAL vertex iterator. + typedef typename std::vector< CGAL_vertex_iterator > Vector_vertex_iterator; private: /** \brief Vertex iterator vector to switch from simplex tree vertex handle to CGAL vertex iterator. @@ -238,14 +238,16 @@ class Alpha_complex { hint = pos->full_cell(); } // -------------------------------------------------------------------------------------------- - // double map to retrieve simplex tree vertex handles from CGAL vertex iterator and vice versa + // structure to retrieve CGAL points from vertex handle - one vertex handle per point. + // Needs to be constructed before as vertex handles arrives in no particular order. + vertex_handle_to_iterator_.resize(point_cloud.size()); // Loop on triangulation vertices list for (CGAL_vertex_iterator vit = triangulation_->vertices_begin(); vit != triangulation_->vertices_end(); ++vit) { if (!triangulation_->is_infinite(*vit)) { #ifdef DEBUG_TRACES std::cout << "Vertex insertion - " << vit->data() << " -> " << vit->point() << std::endl; #endif // DEBUG_TRACES - vertex_handle_to_iterator_.emplace(vit->data(), vit); + vertex_handle_to_iterator_[vit->data()] = vit; } } // -------------------------------------------------------------------------------------------- -- cgit v1.2.3 From 10e7b9ff243260887c54c5ca49d92a27c281c68f Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 3 Dec 2019 17:45:14 +0100 Subject: Fix #118 - protect some bottleneck tests --- .../test/bottleneck_unit_test.cpp | 37 +++++++++++----------- src/common/include/gudhi/Unitary_tests_utils.h | 11 +++++++ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/Bottleneck_distance/test/bottleneck_unit_test.cpp b/src/Bottleneck_distance/test/bottleneck_unit_test.cpp index 3fc6fc7b..2c520045 100644 --- a/src/Bottleneck_distance/test/bottleneck_unit_test.cpp +++ b/src/Bottleneck_distance/test/bottleneck_unit_test.cpp @@ -15,6 +15,7 @@ #include #include +#include using namespace Gudhi::persistence_diagram; @@ -59,24 +60,24 @@ BOOST_AUTO_TEST_CASE(persistence_graph) { BOOST_CHECK(g.size() == (n1 + n2)); // BOOST_CHECK((int) d.size() == (n1 + n2)*(n1 + n2) + n1 + n2 + 1); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(0, 0)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(0, n1 - 1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(0, n1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(0, n2 - 1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(0, n2)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(0, (n1 + n2) - 1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(n1, 0)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(n1, n1 - 1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(n1, n1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(n1, n2 - 1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(n1, n2)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance(n1, (n1 + n2) - 1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance((n1 + n2) - 1, 0)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance((n1 + n2) - 1, n1 - 1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance((n1 + n2) - 1, n1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance((n1 + n2) - 1, n2 - 1)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance((n1 + n2) - 1, n2)) > 0); - BOOST_CHECK(std::count(d.begin(), d.end(), g.distance((n1 + n2) - 1, (n1 + n2) - 1)) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(0, 0))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(0, n1 - 1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(0, n1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(0, n2 - 1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(0, n2))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(0, (n1 + n2) - 1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(n1, 0))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(n1, n1 - 1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(n1, n1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(n1, n2 - 1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(n1, n2))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance(n1, (n1 + n2) - 1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance((n1 + n2) - 1, 0))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance((n1 + n2) - 1, n1 - 1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance((n1 + n2) - 1, n1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance((n1 + n2) - 1, n2 - 1))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance((n1 + n2) - 1, n2))) > 0); + BOOST_CHECK(std::count(d.begin(), d.end(), GUDHI_PROTECT_FLOAT(g.distance((n1 + n2) - 1, (n1 + n2) - 1))) > 0); } BOOST_AUTO_TEST_CASE(neighbors_finder) { diff --git a/src/common/include/gudhi/Unitary_tests_utils.h b/src/common/include/gudhi/Unitary_tests_utils.h index 7d039304..9b86460a 100644 --- a/src/common/include/gudhi/Unitary_tests_utils.h +++ b/src/common/include/gudhi/Unitary_tests_utils.h @@ -26,4 +26,15 @@ void GUDHI_TEST_FLOAT_EQUALITY_CHECK(FloatingType a, FloatingType b, BOOST_CHECK(std::fabs(a - b) <= epsilon); } +// That's the usual x86 issue where a+b==a+b can return false (without any NaN) because one of them was stored in +// memory (and thus rounded to 64 bits) while the other is still in a register (80 bits). +template +FloatingType GUDHI_PROTECT_FLOAT(FloatingType value) { + volatile FloatingType protected_value = value; +#ifdef DEBUG_TRACES + std::cout << "GUDHI_PROTECT_FLOAT - " << protected_value << std::endl; +#endif + return protected_value; +} + #endif // UNITARY_TESTS_UTILS_H_ -- cgit v1.2.3 From 9fc45aafa4588b86ccd903b9173a8cdf48db68a1 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 5 Dec 2019 16:01:45 +0100 Subject: Add MPFR dependency for python module. Use Epeck_d by default for Alpha complex. Add boolean exact version mechanism --- src/cmake/modules/FindMPFR.cmake | 51 ++++++++++++++++++ .../modules/GUDHI_third_party_libraries.cmake | 2 + src/python/CMakeLists.txt | 61 +++++++++++++--------- src/python/gudhi/alpha_complex.pyx | 13 +++-- src/python/include/Alpha_complex_interface.h | 9 ++-- 5 files changed, 102 insertions(+), 34 deletions(-) create mode 100644 src/cmake/modules/FindMPFR.cmake diff --git a/src/cmake/modules/FindMPFR.cmake b/src/cmake/modules/FindMPFR.cmake new file mode 100644 index 00000000..6c963272 --- /dev/null +++ b/src/cmake/modules/FindMPFR.cmake @@ -0,0 +1,51 @@ +# Try to find the MPFR libraries +# MPFR_FOUND - system has MPFR lib +# MPFR_INCLUDE_DIR - the MPFR include directory +# MPFR_LIBRARIES_DIR - Directory where the MPFR libraries are located +# MPFR_LIBRARIES - the MPFR libraries + +# TODO: support MacOSX + +include(FindPackageHandleStandardArgs) + +if(MPFR_INCLUDE_DIR) + set(MPFR_in_cache TRUE) +else() + set(MPFR_in_cache FALSE) +endif() +if(NOT MPFR_LIBRARIES) + set(MPFR_in_cache FALSE) +endif() + +# Is it already configured? +if (MPFR_in_cache) + set(MPFR_FOUND TRUE) +else() + find_path(MPFR_INCLUDE_DIR + NAMES mpfr.h + HINTS ENV MPFR_INC_DIR + ENV MPFR_DIR + ${CGAL_INSTALLATION_PACKAGE_DIR}/auxiliary/gmp/include + PATH_SUFFIXES include + DOC "The directory containing the MPFR header files" + ) + + find_library(MPFR_LIBRARIES NAMES mpfr libmpfr-4 libmpfr-1 + HINTS ENV MPFR_LIB_DIR + ENV MPFR_DIR + ${CGAL_INSTALLATION_PACKAGE_DIR}/auxiliary/gmp/lib + PATH_SUFFIXES lib + DOC "Path to the MPFR library" + ) + + if ( MPFR_LIBRARIES ) + get_filename_component(MPFR_LIBRARIES_DIR ${MPFR_LIBRARIES} PATH CACHE ) + endif() + + # Attempt to load a user-defined configuration for MPFR if couldn't be found + if ( NOT MPFR_INCLUDE_DIR OR NOT MPFR_LIBRARIES_DIR ) + include( MPFRConfig OPTIONAL ) + endif() + + find_package_handle_standard_args(MPFR "DEFAULT_MSG" MPFR_LIBRARIES MPFR_INCLUDE_DIR) +endif() diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index 24a34150..d8c7a428 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -6,6 +6,8 @@ if(NOT Boost_FOUND) message(FATAL_ERROR "NOTICE: This program requires Boost and will not be compiled.") endif(NOT Boost_FOUND) +find_package(MPFR) + find_package(GMP) if(GMP_FOUND) INCLUDE_DIRECTORIES(${GMP_INCLUDE_DIR}) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 9af85eac..13a8a909 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -114,9 +114,9 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_MODULES_TO_COMPILE "${GUDHI_PYTHON_MODULES_TO_COMPILE}'nerve_gic', ") endif () if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) + set(GUDHI_PYTHON_MODULES_TO_COMPILE "${GUDHI_PYTHON_MODULES_TO_COMPILE}'alpha_complex', ") set(GUDHI_PYTHON_MODULES_TO_COMPILE "${GUDHI_PYTHON_MODULES_TO_COMPILE}'subsampling', ") set(GUDHI_PYTHON_MODULES_TO_COMPILE "${GUDHI_PYTHON_MODULES_TO_COMPILE}'tangential_complex', ") - set(GUDHI_PYTHON_MODULES_TO_COMPILE "${GUDHI_PYTHON_MODULES_TO_COMPILE}'alpha_complex', ") set(GUDHI_PYTHON_MODULES_TO_COMPILE "${GUDHI_PYTHON_MODULES_TO_COMPILE}'euclidean_witness_complex', ") set(GUDHI_PYTHON_MODULES_TO_COMPILE "${GUDHI_PYTHON_MODULES_TO_COMPILE}'euclidean_strong_witness_complex', ") endif () @@ -162,10 +162,17 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_USE_GMPXX', ") add_GUDHI_PYTHON_lib("${GMPXX_LIBRARIES}") set(GUDHI_PYTHON_LIBRARY_DIRS "${GUDHI_PYTHON_LIBRARY_DIRS}'${GMPXX_LIBRARIES_DIR}', ") - message("** Add gmpxx ${GMPXX_LIBRARIES_DIR}") + message("** Add gmpxx ${GMPXX_LIBRARIES_DIR}") endif(GMPXX_FOUND) endif(GMP_FOUND) - endif(CGAL_FOUND) + if(MPFR_FOUND) + add_gudhi_debug_info("MPFR_LIBRARIES = ${MPFR_LIBRARIES}") + set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_USE_MPFR', ") + add_GUDHI_PYTHON_lib("${MPFR_LIBRARIES}") + set(GUDHI_PYTHON_LIBRARY_DIRS "${GUDHI_PYTHON_LIBRARY_DIRS}'${MPFR_LIBRARIES_DIR}', ") + message("** Add mpfr ${MPFR_LIBRARIES}") + endif(MPFR_FOUND) +endif(CGAL_FOUND) # Specific for Mac if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") @@ -223,13 +230,14 @@ if(PYTHONINTERP_FOUND) # Test examples if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - # Bottleneck and Alpha - add_test(NAME alpha_rips_persistence_bottleneck_distance_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_rips_persistence_bottleneck_distance.py" - -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -t 0.15 -d 3) - + if (MPFR_FOUND) + # Bottleneck and Alpha + add_test(NAME alpha_rips_persistence_bottleneck_distance_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_rips_persistence_bottleneck_distance.py" + -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -t 0.15 -d 3) + endif(MPFR_FOUND) if(MATPLOTLIB_FOUND AND NUMPY_FOUND) # Tangential add_test(NAME tangential_complex_plain_homology_from_off_file_example_py_test @@ -300,22 +308,23 @@ if(PYTHONINTERP_FOUND) endif (NOT CGAL_VERSION VERSION_LESS 4.11.0) if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - # Alpha - add_test(NAME alpha_complex_from_points_example_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_from_points_example.py") - - if(MATPLOTLIB_FOUND AND NUMPY_FOUND) - add_test(NAME alpha_complex_diagram_persistence_from_off_file_example_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_diagram_persistence_from_off_file_example.py" - --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 0.6) - endif() - - add_gudhi_py_test(test_alpha_complex) - + if (MPFR_FOUND) + # Alpha + add_test(NAME alpha_complex_from_points_example_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_from_points_example.py") + + if(MATPLOTLIB_FOUND AND NUMPY_FOUND) + add_test(NAME alpha_complex_diagram_persistence_from_off_file_example_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_diagram_persistence_from_off_file_example.py" + --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 0.6) + endif() + + add_gudhi_py_test(test_alpha_complex) + endif(MPFR_FOUND) endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index 8f2c98d5..abac6e7b 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -28,7 +28,7 @@ cdef extern from "Alpha_complex_interface.h" namespace "Gudhi": # bool from_file is a workaround for cython to find the correct signature Alpha_complex_interface(string off_file, bool from_file) vector[double] get_point(int vertex) - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square) + void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square, bool exact_version) # AlphaComplex python interface cdef class AlphaComplex: @@ -66,7 +66,7 @@ cdef class AlphaComplex: """ # The real cython constructor - def __cinit__(self, points=None, off_file=''): + def __cinit__(self, points = None, off_file = ''): if off_file: if os.path.isfile(off_file): self.thisptr = new Alpha_complex_interface(str.encode(off_file), True) @@ -99,17 +99,22 @@ cdef class AlphaComplex: cdef vector[double] point = self.thisptr.get_point(vertex) return point - def create_simplex_tree(self, max_alpha_square=float('inf')): + def create_simplex_tree(self, max_alpha_square = float('inf'), + exact_version = False): """ :param max_alpha_square: The maximum alpha square threshold the simplices shall not exceed. Default is set to infinity, and there is very little point using anything else since it does not save time. :type max_alpha_square: float + :param exact_version: Exact computation version. Default is false + which means Safe version is used. + :type exact_version: bool :returns: A simplex tree created from the Delaunay Triangulation. :rtype: SimplexTree """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square) + self.thisptr.create_simplex_tree(stree_int_ptr, + max_alpha_square, exact_version) return stree diff --git a/src/python/include/Alpha_complex_interface.h b/src/python/include/Alpha_complex_interface.h index 96353cc4..a7621f2b 100644 --- a/src/python/include/Alpha_complex_interface.h +++ b/src/python/include/Alpha_complex_interface.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -28,7 +29,7 @@ namespace Gudhi { namespace alpha_complex { class Alpha_complex_interface { - using Dynamic_kernel = CGAL::Epick_d< CGAL::Dynamic_dimension_tag >; + using Dynamic_kernel = CGAL::Epeck_d< CGAL::Dynamic_dimension_tag >; using Point_d = Dynamic_kernel::Point_d; public: @@ -51,7 +52,7 @@ class Alpha_complex_interface { std::vector vd; try { Point_d const& ph = alpha_complex_->get_point(vh); - for (auto coord = ph.cartesian_begin(); coord < ph.cartesian_end(); coord++) + for (auto coord = ph.cartesian_begin(); coord != ph.cartesian_end(); coord++) vd.push_back(CGAL::to_double(*coord)); } catch (std::out_of_range const&) { // std::out_of_range is thrown in case not found. Other exceptions must be re-thrown @@ -59,8 +60,8 @@ class Alpha_complex_interface { return vd; } - void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square) { - alpha_complex_->create_complex(*simplex_tree, max_alpha_square); + void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, bool exact_version) { + alpha_complex_->create_complex(*simplex_tree, max_alpha_square, exact_version); simplex_tree->initialize_filtration(); } -- cgit v1.2.3 From 3757fedbf7ad387cdc06870a1db9532bbee4bab9 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 5 Dec 2019 17:05:19 +0100 Subject: Add a test that fixes #107 --- src/python/test/test_alpha_complex.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/python/test/test_alpha_complex.py b/src/python/test/test_alpha_complex.py index 24f8bf53..ab84daaa 100755 --- a/src/python/test/test_alpha_complex.py +++ b/src/python/test/test_alpha_complex.py @@ -1,4 +1,8 @@ from gudhi import AlphaComplex, SimplexTree +import math +import numpy as np +import itertools +import pytest """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -88,3 +92,34 @@ def test_filtered_alpha(): ] assert simplex_tree.get_star([0]) == [([0], 0.0), ([0, 1], 0.25), ([0, 2], 0.25)] assert simplex_tree.get_cofaces([0], 1) == [([0, 1], 0.25), ([0, 2], 0.25)] + +def alpha_persistence_comparison(exact_version): + #generate periodic signal + time = np.arange(0, 10, 1) + signal = [math.sin(x) for x in time] + delta = math.pi + delayed = [math.sin(x + delta) for x in time] + + #construct embedding + embedding1 = [[signal[i], -signal[i]] for i in range(len(time))] + embedding2 = [[signal[i], delayed[i]] for i in range(len(time))] + + #build alpha complex and simplex tree + alpha_complex1 = AlphaComplex(points=embedding1) + simplex_tree1 = alpha_complex1.create_simplex_tree(exact_version = exact_version) + + alpha_complex2 = AlphaComplex(points=embedding2) + simplex_tree2 = alpha_complex2.create_simplex_tree(exact_version = exact_version) + + diag1 = simplex_tree1.persistence() + diag2 = simplex_tree2.persistence() + + for (first_p, second_p) in itertools.zip_longest(diag1, diag2): + assert first_p[0] == pytest.approx(second_p[0]) + assert first_p[1] == pytest.approx(second_p[1]) + +def test_exact_alpha_version(): + alpha_persistence_comparison(exact_version = True) + +def test_safe_alpha_version(): + alpha_persistence_comparison(exact_version = False) -- cgit v1.2.3 From 6142e146e92801cafe1438bf487f3ecafa502177 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 5 Dec 2019 17:57:19 +0100 Subject: nicer doc presentation --- src/python/gudhi/alpha_complex.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index abac6e7b..bfb9783a 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -107,8 +107,8 @@ cdef class AlphaComplex: there is very little point using anything else since it does not save time. :type max_alpha_square: float - :param exact_version: Exact computation version. Default is false - which means Safe version is used. + :param exact_version: :code:`EXACT` computation version if set. + Default is false which means :code:`SAFE` version is used. :type exact_version: bool :returns: A simplex tree created from the Delaunay Triangulation. :rtype: SimplexTree -- cgit v1.2.3 From 12130250d8c25db81a0806dc7444b47c3030315b Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 6 Dec 2019 13:14:25 +0100 Subject: sphinx module shall work as travis updated it with 2.2.1 version --- .travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index fd87b083..fc7a5207 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,10 +23,9 @@ matrix: - env: # 4. Only doxygen documentation - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='doxygen' - # Issue with sphinx-build with sphinx 2.0.1 - # - env: - # # 5. Only Python, associated tests and sphinx documentation - # - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='test sphinx' + - env: + # 5. Only Python, associated tests and sphinx documentation + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='test sphinx' cache: directories: -- cgit v1.2.3 From 40de7eb3301f95aa9a1e7401857f70a4f7e4b5db Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 6 Dec 2019 14:11:19 +0100 Subject: Add scikit-learn to build python doc --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fc7a5207..0a4a4cb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,7 @@ before_cache: # When installing through libcgal-dev apt, CMake Error at CGAL Exports.cmake The imported target "CGAL::CGAL Qt5" references the file install: - python3 -m pip install --upgrade pip setuptools wheel - - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex sphinx-paramlinks matplotlib numpy scipy + - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex sphinx-paramlinks matplotlib numpy scipy scikit-learn - python3 -m pip install --user POT script: -- cgit v1.2.3 From 3e829fd6f4a3a122da9df35a88e5c51122860bf6 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 6 Dec 2019 15:04:49 +0100 Subject: Rollback as sphinx does not want to build. No output so it is hard to debug --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0a4a4cb8..f178e9ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,9 +23,10 @@ matrix: - env: # 4. Only doxygen documentation - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='doxygen' - - env: - # 5. Only Python, associated tests and sphinx documentation - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='test sphinx' + # Issue with sphinx-build with sphinx 2.0.1 + # - env: + # # 5. Only Python, associated tests and sphinx documentation + # - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='test sphinx' cache: directories: -- cgit v1.2.3 From 17cf8da254d88fa42cbe9e7bb486147def47c26b Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 6 Dec 2019 15:27:15 +0100 Subject: Modify boost tests to be more verbose and errors in colour. I kept coverage tests (was only used by Jenkins) and they still can be activated through an option --- src/Alpha_complex/test/CMakeLists.txt | 22 +++++++-------- src/Bitmap_cubical_complex/test/CMakeLists.txt | 5 ++-- src/Bottleneck_distance/test/CMakeLists.txt | 5 ++-- src/Cech_complex/test/CMakeLists.txt | 5 ++-- src/Nerve_GIC/test/CMakeLists.txt | 5 ++-- .../test/CMakeLists.txt | 33 ++++++---------------- src/Persistent_cohomology/test/CMakeLists.txt | 13 ++++----- src/Rips_complex/test/CMakeLists.txt | 5 ++-- src/Simplex_tree/test/CMakeLists.txt | 18 ++++-------- src/Skeleton_blocker/test/CMakeLists.txt | 11 +++----- src/Spatial_searching/test/CMakeLists.txt | 9 ++---- src/Subsampling/test/CMakeLists.txt | 14 ++++----- src/Tangential_complex/test/CMakeLists.txt | 6 ++-- src/Toplex_map/test/CMakeLists.txt | 8 ++---- src/Witness_complex/test/CMakeLists.txt | 8 ++---- src/cmake/modules/GUDHI_boost_test.cmake | 26 +++++++++++++++++ src/cmake/modules/GUDHI_compilation_flags.cmake | 2 ++ src/cmake/modules/GUDHI_test_coverage.cmake | 26 ----------------- src/common/test/CMakeLists.txt | 13 +++------ 19 files changed, 95 insertions(+), 139 deletions(-) create mode 100644 src/cmake/modules/GUDHI_boost_test.cmake delete mode 100644 src/cmake/modules/GUDHI_test_coverage.cmake diff --git a/src/Alpha_complex/test/CMakeLists.txt b/src/Alpha_complex/test/CMakeLists.txt index ad5b6314..0476c6d4 100644 --- a/src/Alpha_complex/test/CMakeLists.txt +++ b/src/Alpha_complex/test/CMakeLists.txt @@ -1,27 +1,27 @@ project(Alpha_complex_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_executable ( Alpha_complex_test_unit Alpha_complex_unit_test.cpp ) - target_link_libraries(Alpha_complex_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Alpha_complex_test_unit ${CGAL_LIBRARY}) if (TBB_FOUND) target_link_libraries(Alpha_complex_test_unit ${TBB_LIBRARIES}) endif() - gudhi_add_coverage_test(Alpha_complex_test_unit) + gudhi_add_boost_test(Alpha_complex_test_unit) add_executable ( Alpha_complex_3d_test_unit Alpha_complex_3d_unit_test.cpp ) - target_link_libraries(Alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Alpha_complex_3d_test_unit ${CGAL_LIBRARY}) add_executable ( Weighted_alpha_complex_3d_test_unit Weighted_alpha_complex_3d_unit_test.cpp ) - target_link_libraries(Weighted_alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Weighted_alpha_complex_3d_test_unit ${CGAL_LIBRARY}) add_executable ( Periodic_alpha_complex_3d_test_unit Periodic_alpha_complex_3d_unit_test.cpp ) - target_link_libraries(Periodic_alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Periodic_alpha_complex_3d_test_unit ${CGAL_LIBRARY}) add_executable ( Weighted_periodic_alpha_complex_3d_test_unit Weighted_periodic_alpha_complex_3d_unit_test.cpp ) - target_link_libraries(Weighted_periodic_alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Weighted_periodic_alpha_complex_3d_test_unit ${CGAL_LIBRARY}) if (TBB_FOUND) target_link_libraries(Alpha_complex_3d_test_unit ${TBB_LIBRARIES}) target_link_libraries(Weighted_alpha_complex_3d_test_unit ${TBB_LIBRARIES}) @@ -29,9 +29,9 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) target_link_libraries(Weighted_periodic_alpha_complex_3d_test_unit ${TBB_LIBRARIES}) endif() - gudhi_add_coverage_test(Alpha_complex_3d_test_unit) - gudhi_add_coverage_test(Weighted_alpha_complex_3d_test_unit) - gudhi_add_coverage_test(Periodic_alpha_complex_3d_test_unit) - gudhi_add_coverage_test(Weighted_periodic_alpha_complex_3d_test_unit) + gudhi_add_boost_test(Alpha_complex_3d_test_unit) + gudhi_add_boost_test(Weighted_alpha_complex_3d_test_unit) + gudhi_add_boost_test(Periodic_alpha_complex_3d_test_unit) + gudhi_add_boost_test(Weighted_periodic_alpha_complex_3d_test_unit) endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/Bitmap_cubical_complex/test/CMakeLists.txt b/src/Bitmap_cubical_complex/test/CMakeLists.txt index d2f002a6..eb7eb6b5 100644 --- a/src/Bitmap_cubical_complex/test/CMakeLists.txt +++ b/src/Bitmap_cubical_complex/test/CMakeLists.txt @@ -1,14 +1,13 @@ project(Bitmap_cubical_complex_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/bitmap/sinusoid.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_executable ( Bitmap_cubical_complex_test_unit Bitmap_test.cpp ) -target_link_libraries(Bitmap_cubical_complex_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Bitmap_cubical_complex_test_unit ${TBB_LIBRARIES}) endif() -gudhi_add_coverage_test(Bitmap_cubical_complex_test_unit) +gudhi_add_boost_test(Bitmap_cubical_complex_test_unit) diff --git a/src/Bottleneck_distance/test/CMakeLists.txt b/src/Bottleneck_distance/test/CMakeLists.txt index ec2d045f..3acd3d86 100644 --- a/src/Bottleneck_distance/test/CMakeLists.txt +++ b/src/Bottleneck_distance/test/CMakeLists.txt @@ -1,14 +1,13 @@ project(Bottleneck_distance_tests) if (NOT CGAL_VERSION VERSION_LESS 4.11.0) - include(GUDHI_test_coverage) + include(GUDHI_boost_test) add_executable ( Bottleneck_distance_test_unit bottleneck_unit_test.cpp ) - target_link_libraries(Bottleneck_distance_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Bottleneck_distance_test_unit ${TBB_LIBRARIES}) endif(TBB_FOUND) - gudhi_add_coverage_test(Bottleneck_distance_test_unit) + gudhi_add_boost_test(Bottleneck_distance_test_unit) endif (NOT CGAL_VERSION VERSION_LESS 4.11.0) diff --git a/src/Cech_complex/test/CMakeLists.txt b/src/Cech_complex/test/CMakeLists.txt index 8db51173..db510af3 100644 --- a/src/Cech_complex/test/CMakeLists.txt +++ b/src/Cech_complex/test/CMakeLists.txt @@ -1,10 +1,9 @@ cmake_minimum_required(VERSION 2.6) project(Cech_complex_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Cech_complex_test_unit test_cech_complex.cpp ) -target_link_libraries(Cech_complex_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Cech_complex_test_unit ${TBB_LIBRARIES}) endif() @@ -12,4 +11,4 @@ endif() # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -gudhi_add_coverage_test(Cech_complex_test_unit) +gudhi_add_boost_test(Cech_complex_test_unit) diff --git a/src/Nerve_GIC/test/CMakeLists.txt b/src/Nerve_GIC/test/CMakeLists.txt index b89c18a2..567bf43f 100644 --- a/src/Nerve_GIC/test/CMakeLists.txt +++ b/src/Nerve_GIC/test/CMakeLists.txt @@ -1,16 +1,15 @@ project(Graph_induced_complex_tests) if (NOT CGAL_VERSION VERSION_LESS 4.11.0) - include(GUDHI_test_coverage) + include(GUDHI_boost_test) add_executable ( Nerve_GIC_test_unit test_GIC.cpp ) - target_link_libraries(Nerve_GIC_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Nerve_GIC_test_unit ${TBB_LIBRARIES}) endif() file(COPY data DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) - gudhi_add_coverage_test(Nerve_GIC_test_unit) + gudhi_add_boost_test(Nerve_GIC_test_unit) endif (NOT CGAL_VERSION VERSION_LESS 4.11.0) diff --git a/src/Persistence_representations/test/CMakeLists.txt b/src/Persistence_representations/test/CMakeLists.txt index a95880c9..92d68a63 100644 --- a/src/Persistence_representations/test/CMakeLists.txt +++ b/src/Persistence_representations/test/CMakeLists.txt @@ -1,51 +1,36 @@ project(Persistence_representations_test) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) # copy data directory for tests purpose. file(COPY data DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_executable ( Persistence_intervals_test_unit persistence_intervals_test.cpp ) -target_link_libraries(Persistence_intervals_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Persistence_intervals_test_unit) +gudhi_add_boost_test(Persistence_intervals_test_unit) add_executable (Vector_representation_test_unit vector_representation_test.cpp ) -target_link_libraries(Vector_representation_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Vector_representation_test_unit) +gudhi_add_boost_test(Vector_representation_test_unit) add_executable (Persistence_lanscapes_test_unit persistence_lanscapes_test.cpp ) -target_link_libraries(Persistence_lanscapes_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Persistence_lanscapes_test_unit) +gudhi_add_boost_test(Persistence_lanscapes_test_unit) add_executable ( Persistence_lanscapes_on_grid_test_unit persistence_lanscapes_on_grid_test.cpp ) -target_link_libraries(Persistence_lanscapes_on_grid_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Persistence_lanscapes_on_grid_test_unit) +gudhi_add_boost_test(Persistence_lanscapes_on_grid_test_unit) add_executable (Persistence_heat_maps_test_unit persistence_heat_maps_test.cpp ) -target_link_libraries(Persistence_heat_maps_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Persistence_heat_maps_test_unit) +gudhi_add_boost_test(Persistence_heat_maps_test_unit) add_executable ( Read_persistence_from_file_test_unit read_persistence_from_file_test.cpp ) -target_link_libraries(Read_persistence_from_file_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(Read_persistence_from_file_test_unit) +gudhi_add_boost_test(Read_persistence_from_file_test_unit) add_executable ( kernels_unit kernels.cpp ) -target_link_libraries(kernels_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - -gudhi_add_coverage_test(kernels_unit) +gudhi_add_boost_test(kernels_unit) if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) add_executable (Persistence_intervals_with_distances_test_unit persistence_intervals_with_distances_test.cpp ) - target_link_libraries(Persistence_intervals_with_distances_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Persistence_intervals_with_distances_test_unit ${TBB_LIBRARIES}) endif(TBB_FOUND) - gudhi_add_coverage_test(Persistence_intervals_with_distances_test_unit) + gudhi_add_boost_test(Persistence_intervals_with_distances_test_unit) endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/Persistent_cohomology/test/CMakeLists.txt b/src/Persistent_cohomology/test/CMakeLists.txt index f8baf861..64669c4e 100644 --- a/src/Persistent_cohomology/test/CMakeLists.txt +++ b/src/Persistent_cohomology/test/CMakeLists.txt @@ -1,11 +1,9 @@ project(Persistent_cohomology_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Persistent_cohomology_test_unit persistent_cohomology_unit_test.cpp ) -target_link_libraries(Persistent_cohomology_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_executable ( Persistent_cohomology_test_betti_numbers betti_numbers_unit_test.cpp ) -target_link_libraries(Persistent_cohomology_test_betti_numbers ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Persistent_cohomology_test_unit ${TBB_LIBRARIES}) target_link_libraries(Persistent_cohomology_test_betti_numbers ${TBB_LIBRARIES}) @@ -16,13 +14,12 @@ file(COPY "${CMAKE_SOURCE_DIR}/src/Persistent_cohomology/test/simplex_tree_file_ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) # Unitary tests -gudhi_add_coverage_test(Persistent_cohomology_test_unit) -gudhi_add_coverage_test(Persistent_cohomology_test_betti_numbers) +gudhi_add_boost_test(Persistent_cohomology_test_unit) +gudhi_add_boost_test(Persistent_cohomology_test_betti_numbers) if(GMPXX_FOUND AND GMP_FOUND) add_executable ( Persistent_cohomology_test_unit_multi_field persistent_cohomology_unit_test_multi_field.cpp ) - target_link_libraries(Persistent_cohomology_test_unit_multi_field - ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${GMPXX_LIBRARIES} ${GMP_LIBRARIES}) + target_link_libraries(Persistent_cohomology_test_unit_multi_field ${GMPXX_LIBRARIES} ${GMP_LIBRARIES}) if (TBB_FOUND) target_link_libraries(Persistent_cohomology_test_unit_multi_field ${TBB_LIBRARIES}) endif(TBB_FOUND) @@ -31,7 +28,7 @@ if(GMPXX_FOUND AND GMP_FOUND) file(COPY "${CMAKE_SOURCE_DIR}/src/Persistent_cohomology/test/simplex_tree_file_for_multi_field_unit_test.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) # Unitary tests - gudhi_add_coverage_test(Persistent_cohomology_test_unit_multi_field) + gudhi_add_boost_test(Persistent_cohomology_test_unit_multi_field) endif(GMPXX_FOUND AND GMP_FOUND) diff --git a/src/Rips_complex/test/CMakeLists.txt b/src/Rips_complex/test/CMakeLists.txt index 745d953c..b359584e 100644 --- a/src/Rips_complex/test/CMakeLists.txt +++ b/src/Rips_complex/test/CMakeLists.txt @@ -1,9 +1,8 @@ project(Rips_complex_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Rips_complex_test_unit test_rips_complex.cpp ) -target_link_libraries(Rips_complex_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Rips_complex_test_unit ${TBB_LIBRARIES}) endif() @@ -12,4 +11,4 @@ endif() file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) file(COPY "${CMAKE_SOURCE_DIR}/data/distance_matrix/full_square_distance_matrix.csv" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -gudhi_add_coverage_test(Rips_complex_test_unit) +gudhi_add_boost_test(Rips_complex_test_unit) diff --git a/src/Simplex_tree/test/CMakeLists.txt b/src/Simplex_tree/test/CMakeLists.txt index 5bea3938..8b9163f5 100644 --- a/src/Simplex_tree/test/CMakeLists.txt +++ b/src/Simplex_tree/test/CMakeLists.txt @@ -1,38 +1,30 @@ project(Simplex_tree_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) # Do not forget to copy test files in current binary dir file(COPY "simplex_tree_for_unit_test.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_executable ( Simplex_tree_test_unit simplex_tree_unit_test.cpp ) -target_link_libraries(Simplex_tree_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Simplex_tree_test_unit ${TBB_LIBRARIES}) endif() - -gudhi_add_coverage_test(Simplex_tree_test_unit) +gudhi_add_boost_test(Simplex_tree_test_unit) add_executable ( Simplex_tree_remove_test_unit simplex_tree_remove_unit_test.cpp ) -target_link_libraries(Simplex_tree_remove_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Simplex_tree_remove_test_unit ${TBB_LIBRARIES}) endif() - -gudhi_add_coverage_test(Simplex_tree_remove_test_unit) +gudhi_add_boost_test(Simplex_tree_remove_test_unit) add_executable ( Simplex_tree_iostream_operator_test_unit simplex_tree_iostream_operator_unit_test.cpp ) -target_link_libraries(Simplex_tree_iostream_operator_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Simplex_tree_iostream_operator_test_unit ${TBB_LIBRARIES}) endif() - -gudhi_add_coverage_test(Simplex_tree_iostream_operator_test_unit) +gudhi_add_boost_test(Simplex_tree_iostream_operator_test_unit) add_executable ( Simplex_tree_ctor_and_move_test_unit simplex_tree_ctor_and_move_unit_test.cpp ) -target_link_libraries(Simplex_tree_ctor_and_move_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Simplex_tree_ctor_and_move_test_unit ${TBB_LIBRARIES}) endif() - -gudhi_add_coverage_test(Simplex_tree_ctor_and_move_test_unit) +gudhi_add_boost_test(Simplex_tree_ctor_and_move_test_unit) diff --git a/src/Skeleton_blocker/test/CMakeLists.txt b/src/Skeleton_blocker/test/CMakeLists.txt index 19c65871..24b6c11e 100644 --- a/src/Skeleton_blocker/test/CMakeLists.txt +++ b/src/Skeleton_blocker/test/CMakeLists.txt @@ -1,17 +1,14 @@ project(Skeleton_blocker_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Skeleton_blocker_test_unit test_skeleton_blocker_complex.cpp ) -target_link_libraries(Skeleton_blocker_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_executable ( Skeleton_blocker_test_geometric_complex test_skeleton_blocker_geometric_complex.cpp ) -target_link_libraries(Skeleton_blocker_test_geometric_complex ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_executable ( Skeleton_blocker_test_simplifiable test_skeleton_blocker_simplifiable.cpp ) -target_link_libraries(Skeleton_blocker_test_simplifiable ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) # Do not forget to copy test files in current binary dir file(COPY "test2.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -gudhi_add_coverage_test(Skeleton_blocker_test_unit) -gudhi_add_coverage_test(Skeleton_blocker_test_geometric_complex) -gudhi_add_coverage_test(Skeleton_blocker_test_simplifiable) +gudhi_add_boost_test(Skeleton_blocker_test_unit) +gudhi_add_boost_test(Skeleton_blocker_test_geometric_complex) +gudhi_add_boost_test(Skeleton_blocker_test_simplifiable) diff --git a/src/Spatial_searching/test/CMakeLists.txt b/src/Spatial_searching/test/CMakeLists.txt index 18f7c6b8..a6c23951 100644 --- a/src/Spatial_searching/test/CMakeLists.txt +++ b/src/Spatial_searching/test/CMakeLists.txt @@ -1,11 +1,8 @@ project(Spatial_searching_tests) if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - include(GUDHI_test_coverage) - + include(GUDHI_boost_test) add_executable( Spatial_searching_test_Kd_tree_search test_Kd_tree_search.cpp ) - target_link_libraries(Spatial_searching_test_Kd_tree_search - ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - - gudhi_add_coverage_test(Spatial_searching_test_Kd_tree_search) + target_link_libraries(Spatial_searching_test_Kd_tree_search ${CGAL_LIBRARY}) + gudhi_add_boost_test(Spatial_searching_test_Kd_tree_search) endif () diff --git a/src/Subsampling/test/CMakeLists.txt b/src/Subsampling/test/CMakeLists.txt index cf54788e..354021c1 100644 --- a/src/Subsampling/test/CMakeLists.txt +++ b/src/Subsampling/test/CMakeLists.txt @@ -1,18 +1,18 @@ project(Subsampling_tests) if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - include(GUDHI_test_coverage) + include(GUDHI_boost_test) add_executable( Subsampling_test_pick_n_random_points test_pick_n_random_points.cpp ) - target_link_libraries(Subsampling_test_pick_n_random_points ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Subsampling_test_pick_n_random_points ${CGAL_LIBRARY}) add_executable( Subsampling_test_choose_n_farthest_points test_choose_n_farthest_points.cpp ) - target_link_libraries(Subsampling_test_choose_n_farthest_points ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Subsampling_test_choose_n_farthest_points ${CGAL_LIBRARY}) add_executable(Subsampling_test_sparsify_point_set test_sparsify_point_set.cpp) - target_link_libraries(Subsampling_test_sparsify_point_set ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Subsampling_test_sparsify_point_set ${CGAL_LIBRARY}) - gudhi_add_coverage_test(Subsampling_test_pick_n_random_points) - gudhi_add_coverage_test(Subsampling_test_choose_n_farthest_points) - gudhi_add_coverage_test(Subsampling_test_sparsify_point_set) + gudhi_add_boost_test(Subsampling_test_pick_n_random_points) + gudhi_add_boost_test(Subsampling_test_choose_n_farthest_points) + gudhi_add_boost_test(Subsampling_test_sparsify_point_set) endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/Tangential_complex/test/CMakeLists.txt b/src/Tangential_complex/test/CMakeLists.txt index ae17a286..2207d67c 100644 --- a/src/Tangential_complex/test/CMakeLists.txt +++ b/src/Tangential_complex/test/CMakeLists.txt @@ -1,13 +1,13 @@ project(Tangential_complex_tests) if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - include(GUDHI_test_coverage) + include(GUDHI_boost_test) add_executable( Tangential_complex_test_TC test_tangential_complex.cpp ) - target_link_libraries(Tangential_complex_test_TC ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + target_link_libraries(Tangential_complex_test_TC ${CGAL_LIBRARY}) if (TBB_FOUND) target_link_libraries(Tangential_complex_test_TC ${TBB_LIBRARIES}) endif() - gudhi_add_coverage_test(Tangential_complex_test_TC) + gudhi_add_boost_test(Tangential_complex_test_TC) endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/Toplex_map/test/CMakeLists.txt b/src/Toplex_map/test/CMakeLists.txt index 59517db5..2997584d 100644 --- a/src/Toplex_map/test/CMakeLists.txt +++ b/src/Toplex_map/test/CMakeLists.txt @@ -1,11 +1,9 @@ project(Toplex_map_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable( Toplex_map_unit_test toplex_map_unit_test.cpp ) -target_link_libraries(Toplex_map_unit_test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) -gudhi_add_coverage_test(Toplex_map_unit_test) +gudhi_add_boost_test(Toplex_map_unit_test) add_executable( Lazy_toplex_map_unit_test lazy_toplex_map_unit_test.cpp ) -target_link_libraries(Lazy_toplex_map_unit_test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) -gudhi_add_coverage_test(Lazy_toplex_map_unit_test) +gudhi_add_boost_test(Lazy_toplex_map_unit_test) diff --git a/src/Witness_complex/test/CMakeLists.txt b/src/Witness_complex/test/CMakeLists.txt index 96188e46..690933aa 100644 --- a/src/Witness_complex/test/CMakeLists.txt +++ b/src/Witness_complex/test/CMakeLists.txt @@ -1,22 +1,20 @@ project(Witness_complex_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Witness_complex_test_simple_witness_complex test_simple_witness_complex.cpp ) -target_link_libraries(Witness_complex_test_simple_witness_complex ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Witness_complex_test_simple_witness_complex ${TBB_LIBRARIES}) endif(TBB_FOUND) -gudhi_add_coverage_test(Witness_complex_test_simple_witness_complex) +gudhi_add_boost_test(Witness_complex_test_simple_witness_complex) # CGAL and Eigen3 are required for Euclidean version of Witness if(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) add_executable ( Witness_complex_test_euclidean_simple_witness_complex test_euclidean_simple_witness_complex.cpp ) - target_link_libraries(Witness_complex_test_euclidean_simple_witness_complex ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) if (TBB_FOUND) target_link_libraries(Witness_complex_test_euclidean_simple_witness_complex ${TBB_LIBRARIES}) endif(TBB_FOUND) - gudhi_add_coverage_test(Witness_complex_test_euclidean_simple_witness_complex) + gudhi_add_boost_test(Witness_complex_test_euclidean_simple_witness_complex) endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) diff --git a/src/cmake/modules/GUDHI_boost_test.cmake b/src/cmake/modules/GUDHI_boost_test.cmake new file mode 100644 index 00000000..c3b29883 --- /dev/null +++ b/src/cmake/modules/GUDHI_boost_test.cmake @@ -0,0 +1,26 @@ +if (WITH_GUDHI_BOOST_TEST_COVERAGE) + # Make CTest output XML coverage report - WITH_GUDHI_BOOST_TEST_COVERAGE must be set - default is OFF + if (GCOVR_PATH) + # for gcovr to make coverage reports - Corbera Jenkins plugin + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + endif() + if (GPROF_PATH) + # for gprof to make coverage reports - Jenkins + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") + endif() + set(GUDHI_UT_LOG_FORMAT "--log_format=XML") + set(GUDHI_UT_LOG_SINK "--log_sink=${CMAKE_BINARY_DIR}/${unitary_test}_UT.xml") + set(GUDHI_UT_LOG_LEVEL "--log_level=test_suite") + set(GUDHI_UT_REPORT_LEVEL "--report_level=no") +else (WITH_GUDHI_BOOST_TEST_COVERAGE) + # Make CTest more verbose and color output + set(GUDHI_UT_LOG_LEVEL "--color_output") + set(GUDHI_UT_REPORT_LEVEL "--report_level=detailed") +endif(WITH_GUDHI_BOOST_TEST_COVERAGE) + +function(gudhi_add_boost_test unitary_test) + target_link_libraries(${unitary_test} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + add_test(NAME ${unitary_test} COMMAND $ + ${GUDHI_UT_LOG_FORMAT} ${GUDHI_UT_LOG_SINK} + ${GUDHI_UT_LOG_LEVEL} ${GUDHI_UT_REPORT_LEVEL}) +endfunction() diff --git a/src/cmake/modules/GUDHI_compilation_flags.cmake b/src/cmake/modules/GUDHI_compilation_flags.cmake index 6cd2614d..34c2e065 100644 --- a/src/cmake/modules/GUDHI_compilation_flags.cmake +++ b/src/cmake/modules/GUDHI_compilation_flags.cmake @@ -73,3 +73,5 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) else() message("++ Release compilation flags are: ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}") endif() + +option(WITH_GUDHI_BOOST_TEST_COVERAGE "Report xml coverage files on boost tests" OFF) diff --git a/src/cmake/modules/GUDHI_test_coverage.cmake b/src/cmake/modules/GUDHI_test_coverage.cmake deleted file mode 100644 index bea5b2d6..00000000 --- a/src/cmake/modules/GUDHI_test_coverage.cmake +++ /dev/null @@ -1,26 +0,0 @@ - -if (GCOVR_PATH) - # for gcovr to make coverage reports - Corbera Jenkins plugin - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") -endif() -if (GPROF_PATH) - # for gprof to make coverage reports - Jenkins - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") -endif() - -if (DEBUG_TRACES) - # Make CTest more verbose with DEBUG_TRACES - no XML output - set(GUDHI_UT_LOG_LEVEL "--log_level=all") - set(GUDHI_UT_REPORT_LEVEL "--report_level=detailed") -else() - set(GUDHI_UT_LOG_FORMAT "--log_format=XML") - set(GUDHI_UT_LOG_SINK "--log_sink=${CMAKE_BINARY_DIR}/${unitary_test}_UT.xml") - set(GUDHI_UT_LOG_LEVEL "--log_level=test_suite") - set(GUDHI_UT_REPORT_LEVEL "--report_level=no") -endif() - -function(gudhi_add_coverage_test unitary_test) - add_test(NAME ${unitary_test} COMMAND $ - ${GUDHI_UT_LOG_FORMAT} ${GUDHI_UT_LOG_SINK} - ${GUDHI_UT_LOG_LEVEL} ${GUDHI_UT_REPORT_LEVEL}) -endfunction() diff --git a/src/common/test/CMakeLists.txt b/src/common/test/CMakeLists.txt index 0b49fa1e..34de7398 100644 --- a/src/common/test/CMakeLists.txt +++ b/src/common/test/CMakeLists.txt @@ -1,15 +1,10 @@ project(Common_tests) -include(GUDHI_test_coverage) +include(GUDHI_boost_test) add_executable ( Common_test_points_off_reader test_points_off_reader.cpp ) -target_link_libraries(Common_test_points_off_reader ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - add_executable ( Common_test_distance_matrix_reader test_distance_matrix_reader.cpp ) -target_link_libraries(Common_test_distance_matrix_reader ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) - add_executable ( Common_test_persistence_intervals_reader test_persistence_intervals_reader.cpp ) -target_link_libraries(Common_test_persistence_intervals_reader ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) # Do not forget to copy test files in current binary dir file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) @@ -19,6 +14,6 @@ file(COPY "${CMAKE_SOURCE_DIR}/src/common/test/persistence_intervals_with_dimens file(COPY "${CMAKE_SOURCE_DIR}/src/common/test/persistence_intervals_with_field.pers" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) file(COPY "${CMAKE_SOURCE_DIR}/src/common/test/persistence_intervals_without_dimension.pers" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) -gudhi_add_coverage_test(Common_test_points_off_reader) -gudhi_add_coverage_test(Common_test_distance_matrix_reader) -gudhi_add_coverage_test(Common_test_persistence_intervals_reader) +gudhi_add_boost_test(Common_test_points_off_reader) +gudhi_add_boost_test(Common_test_distance_matrix_reader) +gudhi_add_boost_test(Common_test_persistence_intervals_reader) -- cgit v1.2.3 From 8827aad479ce7c8171c76c679d75c10acf185c6d Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Sat, 7 Dec 2019 07:47:40 +0100 Subject: Let's see if the error is verbosed --- .travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index f178e9ac..0a4a4cb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,10 +23,9 @@ matrix: - env: # 4. Only doxygen documentation - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='doxygen' - # Issue with sphinx-build with sphinx 2.0.1 - # - env: - # # 5. Only Python, associated tests and sphinx documentation - # - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='test sphinx' + - env: + # 5. Only Python, associated tests and sphinx documentation + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='test sphinx' cache: directories: -- cgit v1.2.3 From ce58cc97866605fe64df479e96d455e90f56f8e2 Mon Sep 17 00:00:00 2001 From: MathieuCarriere Date: Sun, 8 Dec 2019 21:22:09 -0500 Subject: fixed useless coordinates in Landscape if min and max are computed from data --- src/python/doc/representations.rst | 25 ++++++++++++++++++++-- .../diagram_vectorizations_distances_kernels.py | 6 +++--- src/python/gudhi/representations/vector_methods.py | 12 ++++++++--- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index b3131a25..b338f7f0 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -8,9 +8,9 @@ Representations manual .. include:: representations_sum.inc -This module, originally named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning tools, in particular scikit-learn. It provides tools, using the scikit-learn standard interface, to compute distances and kernels on diagrams, and to convert diagrams into vectors. +This module, originally available at https://github.com/MathieuCarriere/sklearn-tda and named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning, by providing implementations of most of the vector representations for persistence diagrams in the literature, in a scikit-learn format. More specifically, it provides tools, using the scikit-learn standard interface, to compute distances and kernels on persistence diagrams, and to convert these diagrams into vectors in Euclidean space. -A diagram is represented as a numpy array of shape (n,2), as can be obtained from :func:`~gudhi.SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. +A diagram is represented as a numpy array of shape (n,2), as can be obtained from `SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. A small example is provided @@ -46,3 +46,24 @@ Metrics :members: :special-members: :show-inheritance: + +Basic example +------------- + +This example computes the first two Landscapes associated to a persistence diagram with four points. The landscapes are evaluated on ten samples, leading to two vectors with ten coordinates each, that are eventually concatenated in order to produce a single vector representation. + +.. testcode:: + + import numpy as np + from gudhi.representations import Landscape + # A single diagram with 4 points + D = np.array([[0.,4.],[1.,2.],[3.,8.],[6.,8.]]) + diags = [D] + l=Landscape(num_landscapes=2,resolution=10).fit_transform(diags) + print(l) + +The output is: + +.. testoutput:: + + [[0. 1.25707872 2.51415744 1.88561808 0.7856742 2.04275292 3.29983165 2.51415744 1.25707872 0. 0. 0. 0.31426968 0. 0.62853936 0. 0. 0.31426968 1.25707872 0. ]] diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py index 119072eb..f777984c 100755 --- a/src/python/example/diagram_vectorizations_distances_kernels.py +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -26,9 +26,9 @@ plt.show() LS = Landscape(resolution=1000) L = LS.fit_transform(diags) -plt.plot(L[0][:1000]) -plt.plot(L[0][1000:2000]) -plt.plot(L[0][2000:3000]) +plt.plot(L[0][:999]) +plt.plot(L[0][999:2*999]) +plt.plot(L[0][2*999:3*999]) plt.title("Landscape") plt.show() diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py index 61c4fb84..083551a4 100644 --- a/src/python/gudhi/representations/vector_methods.py +++ b/src/python/gudhi/representations/vector_methods.py @@ -104,10 +104,11 @@ class Landscape(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ + self.nan_in_range = np.isnan(np.array(self.sample_range)) if np.isnan(np.array(self.sample_range)).any(): pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] - self.sample_range = np.where(np.isnan(np.array(self.sample_range)), np.array([mx, My]), np.array(self.sample_range)) + self.sample_range = np.where(self.nan_in_range, np.array([mx, My]), np.array(self.sample_range)) return self def transform(self, X): @@ -121,7 +122,7 @@ class Landscape(BaseEstimator, TransformerMixin): numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**): output persistence landscapes. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution + self.nan_in_range.sum()) step_x = x_values[1] - x_values[0] for i in range(num_diag): @@ -157,7 +158,12 @@ class Landscape(BaseEstimator, TransformerMixin): for k in range( min(self.num_landscapes, len(events[j])) ): ls[k,j] = events[j][k] - Xfit.append(np.sqrt(2)*np.reshape(ls,[1,-1])) + if self.nan_in_range[0]: + ls = ls[:,1:] + if self.nan_in_range[1]: + ls = ls[:,:-1] + ls = np.sqrt(2)*np.reshape(ls,[1,-1]) + Xfit.append(ls) Xfit = np.concatenate(Xfit,0) -- cgit v1.2.3 From e668d175dedf05dfcd04cb0cc0a6f0774d92cee6 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 09:22:51 +0100 Subject: use 'ctest --output-on-failure' for unitary tests --- .appveyor.yml | 4 ++-- .circleci/config.yml | 13 +++++++++---- .travis.yml | 13 +++++++------ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 125f3cf4..4a76ea0a 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -57,8 +57,8 @@ build_script: - if [%target%]==[Python] ( cd src/python & MSBuild Cython.sln /m /p:Configuration=Release /p:Platform=x64 & - ctest -j 1 -C Release + ctest -j 1 --output-on-failure -C Release ) else ( MSBuild GUDHIdev.sln /m /p:Configuration=Release /p:Platform=x64 & - ctest -j 1 -C Release -E diff_files + ctest -j 1 --output-on-failure -C Release -E diff_files ) diff --git a/.circleci/config.yml b/.circleci/config.yml index 52cb3d45..5e45bc14 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,7 +11,8 @@ jobs: mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=ON -DWITH_GUDHI_TEST=OFF -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=OFF ..; - make all test; + make all; + ctest --output-on-failure; tests: docker: @@ -24,7 +25,8 @@ jobs: mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_TEST=ON -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=OFF ..; - make all test; + make all; + ctest --output-on-failure; utils: docker: @@ -37,7 +39,8 @@ jobs: mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_TEST=OFF -DWITH_GUDHI_UTILITIES=ON -DWITH_GUDHI_PYTHON=OFF ..; - make all test; + make all; + ctest --output-on-failure; python: docker: @@ -55,7 +58,9 @@ jobs: mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=ON -DPython_ADDITIONAL_VERSIONS=3 ..; - make all test sphinx; + make all; + ctest --output-on-failure; + make sphinx; cp -R python/sphinx /tmp/sphinx; - store_artifacts: diff --git a/.travis.yml b/.travis.yml index 0a4a4cb8..51bc26c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,19 +13,19 @@ matrix: include: - env: # 1. Only examples and associated tests - - CMAKE_EXAMPLE='ON' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='test' + - CMAKE_EXAMPLE='ON' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='all' CTEST_COMMAND='ctest --output-on-failure' - env: # 2. Only unitary tests - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='ON' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='test' + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='ON' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='all' CTEST_COMMAND='ctest --output-on-failure' - env: # 3. Only utilities and associated tests - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='ON' CMAKE_PYTHON='OFF' MAKE_TARGET='test' + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='ON' CMAKE_PYTHON='OFF' MAKE_TARGET='all' CTEST_COMMAND='ctest --output-on-failure' - env: # 4. Only doxygen documentation - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='doxygen' + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='doxygen' CTEST_COMMAND='echo No tests for doxygen target' - env: # 5. Only Python, associated tests and sphinx documentation - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='test sphinx' + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='all sphinx' CTEST_COMMAND='ctest --output-on-failure' cache: directories: @@ -62,7 +62,8 @@ script: - mkdir -p build - cd build - cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=${CMAKE_EXAMPLE} -DWITH_GUDHI_TEST=${CMAKE_TEST} -DWITH_GUDHI_UTILITIES=${CMAKE_UTILITIES} -DWITH_GUDHI_PYTHON=${CMAKE_PYTHON} -DUSER_VERSION_DIR=version -DPython_ADDITIONAL_VERSIONS=3 .. - - make all ${MAKE_TARGET} + - make ${MAKE_TARGET} + - ${CTEST_COMMAND} - cd .. notifications: -- cgit v1.2.3 From 5385b57782d63cf86048762e9a1c9b0c1070930c Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 10:29:02 +0100 Subject: Document 'ctest --output-on-failure' in installation tests section --- src/common/doc/installation.h | 5 ++++- src/python/doc/installation.rst | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index c320e7e0..c4ca10d1 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -32,7 +32,10 @@ make \endverbatim * * \subsection testsuites Test suites * To test your build, run the following command in a terminal: - * \verbatim make test \endverbatim + * \verbatim make test \endverbatim + * `make test` is using Ctest<\a> (CMake test driver + * program). If some of the tests are failing, plend send us the result of the following command: + * \verbatim ctest --output-on-failure \endverbatim * * \subsection documentationgeneration Documentation * To generate the documentation, Doxygen is required. diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 54504413..3553ae51 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -98,6 +98,18 @@ following command in a terminal: export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/python' make test +`make test` is using +`Ctest `_ (CMake test +driver program). If some of the tests are failing, plend send us the result of +the following command: + +.. code-block:: bash + + cd /path-to-gudhi/build/python + # For windows, you have to set PYTHONPATH environment variable + export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/python' + ctest --output-on-failure + Debugging issues ================ -- cgit v1.2.3 From 406d7349a4732a185374822bf465f914e95d07c9 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 11:11:00 +0100 Subject: Add some debug traces to find why sphinx fails --- .travis.yml | 3 ++- src/python/doc/python3-sphinx-build.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 51bc26c3..1c490b69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ matrix: - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='doxygen' CTEST_COMMAND='echo No tests for doxygen target' - env: # 5. Only Python, associated tests and sphinx documentation - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='all sphinx' CTEST_COMMAND='ctest --output-on-failure' + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='all' CTEST_COMMAND='ctest --output-on-failure' cache: directories: @@ -56,6 +56,7 @@ install: - python3 -m pip install --upgrade pip setuptools wheel - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex sphinx-paramlinks matplotlib numpy scipy scikit-learn - python3 -m pip install --user POT + - which python3 script: - rm -rf build diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py index 84d158cf..a8eede8a 100755 --- a/src/python/doc/python3-sphinx-build.py +++ b/src/python/doc/python3-sphinx-build.py @@ -5,6 +5,8 @@ Emulate sphinx-build for python3 """ from sys import exit, argv +print(sys.executable) +import sphinx from sphinx import main if __name__ == '__main__': -- cgit v1.2.3 From 3f99285cfbafd7c9fcabad6469bf16b3ba52396f Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 13:31:18 +0100 Subject: need to import sys --- src/python/doc/python3-sphinx-build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py index a8eede8a..5dc26fd2 100755 --- a/src/python/doc/python3-sphinx-build.py +++ b/src/python/doc/python3-sphinx-build.py @@ -5,6 +5,7 @@ Emulate sphinx-build for python3 """ from sys import exit, argv +import sys print(sys.executable) import sphinx from sphinx import main -- cgit v1.2.3 From 83867c78935460f727831b54e390c5be30bb4eee Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 13:59:07 +0100 Subject: need to import sys --- src/python/doc/python3-sphinx-build.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py index 5dc26fd2..3628e89e 100755 --- a/src/python/doc/python3-sphinx-build.py +++ b/src/python/doc/python3-sphinx-build.py @@ -4,9 +4,8 @@ Emulate sphinx-build for python3 """ -from sys import exit, argv -import sys -print(sys.executable) +from sys import exit, argv, executable +print(executable) import sphinx from sphinx import main -- cgit v1.2.3 From f51fbbcde2f09f977198acfb8aee557ba1bab793 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 15:05:07 +0100 Subject: Hard set python path as cmake does not find the one used by the yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1c490b69..96eb41ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,7 +62,7 @@ script: - rm -rf build - mkdir -p build - cd build - - cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=${CMAKE_EXAMPLE} -DWITH_GUDHI_TEST=${CMAKE_TEST} -DWITH_GUDHI_UTILITIES=${CMAKE_UTILITIES} -DWITH_GUDHI_PYTHON=${CMAKE_PYTHON} -DUSER_VERSION_DIR=version -DPython_ADDITIONAL_VERSIONS=3 .. + - cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=${CMAKE_EXAMPLE} -DWITH_GUDHI_TEST=${CMAKE_TEST} -DWITH_GUDHI_UTILITIES=${CMAKE_UTILITIES} -DWITH_GUDHI_PYTHON=${CMAKE_PYTHON} -DUSER_VERSION_DIR=version -DPYTHON_EXECUTABLE:FILEPATH=/usr/local/bin/python3 -DPython_ADDITIONAL_VERSIONS=3 .. - make ${MAKE_TARGET} - ${CTEST_COMMAND} - cd .. -- cgit v1.2.3 From 9f9663ccee928ba9d73c970e4e174aff9043bb76 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 9 Dec 2019 22:20:37 +0100 Subject: Add some comments of what fails and make the tests pass --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 96eb41ac..b7cd854a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,11 @@ matrix: - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='doxygen' CTEST_COMMAND='echo No tests for doxygen target' - env: # 5. Only Python, associated tests and sphinx documentation - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='all' CTEST_COMMAND='ctest --output-on-failure' + # $ which python3 => /usr/local/bin/python3 + # cmake => -- Found PythonInterp: /usr/local/bin/python3 (found version "3.7.5") + # In python3-sphinx-build.py, print(sys.executable) => /usr/local/opt/python/bin/python3.7 ??? + # should be : MAKE_TARGET='all sphinx' CTEST_COMMAND='ctest --output-on-failure' + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='all' CTEST_COMMAND='ctest --output-on-failure -E sphinx' cache: directories: @@ -62,7 +66,7 @@ script: - rm -rf build - mkdir -p build - cd build - - cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=${CMAKE_EXAMPLE} -DWITH_GUDHI_TEST=${CMAKE_TEST} -DWITH_GUDHI_UTILITIES=${CMAKE_UTILITIES} -DWITH_GUDHI_PYTHON=${CMAKE_PYTHON} -DUSER_VERSION_DIR=version -DPYTHON_EXECUTABLE:FILEPATH=/usr/local/bin/python3 -DPython_ADDITIONAL_VERSIONS=3 .. + - cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=${CMAKE_EXAMPLE} -DWITH_GUDHI_TEST=${CMAKE_TEST} -DWITH_GUDHI_UTILITIES=${CMAKE_UTILITIES} -DWITH_GUDHI_PYTHON=${CMAKE_PYTHON} -DUSER_VERSION_DIR=version -DPython_ADDITIONAL_VERSIONS=3 .. - make ${MAKE_TARGET} - ${CTEST_COMMAND} - cd .. -- cgit v1.2.3 From 4391bf38f14f483b9032e3eaf99f315f2f053026 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 10 Dec 2019 00:04:02 +0100 Subject: Remove debug traces --- .travis.yml | 1 - src/python/doc/python3-sphinx-build.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index b7cd854a..d6c82e70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,6 @@ install: - python3 -m pip install --upgrade pip setuptools wheel - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex sphinx-paramlinks matplotlib numpy scipy scikit-learn - python3 -m pip install --user POT - - which python3 script: - rm -rf build diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py index 3628e89e..d1f0f08e 100755 --- a/src/python/doc/python3-sphinx-build.py +++ b/src/python/doc/python3-sphinx-build.py @@ -4,8 +4,7 @@ Emulate sphinx-build for python3 """ -from sys import exit, argv, executable -print(executable) +from sys import exit, argv import sphinx from sphinx import main -- cgit v1.2.3 From 329637d2a0a806955a29faccf38915c3fb7cd2fd Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 10 Dec 2019 10:03:53 +0100 Subject: Remove number_of_vertices method from Alpha_complex as there is no added value to Simplex_tree.num_vertices() --- src/Alpha_complex/include/gudhi/Alpha_complex.h | 8 --- src/Alpha_complex/test/Alpha_complex_unit_test.cpp | 60 +++++++++++++++------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h index 6f19cb6c..f2a05e95 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h @@ -191,14 +191,6 @@ class Alpha_complex { return vertex_handle_to_iterator_.at(vertex)->point(); } - /** \brief number_of_vertices returns the number of vertices (same as the number of points). - * - * @return The number of vertices. - */ - std::size_t number_of_vertices() const { - return vertex_handle_to_iterator_.size(); - } - private: template void init_from_range(const InputPointRange& points) { diff --git a/src/Alpha_complex/test/Alpha_complex_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_unit_test.cpp index 40b3fe09..27b671dd 100644 --- a/src/Alpha_complex/test/Alpha_complex_unit_test.cpp +++ b/src/Alpha_complex/test/Alpha_complex_unit_test.cpp @@ -53,20 +53,12 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_OFF_file, TestedKernel, list_of Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_name); - std::cout << "alpha_complex_from_points.number_of_vertices()=" << alpha_complex_from_file.number_of_vertices() - << std::endl; - BOOST_CHECK(alpha_complex_from_file.number_of_vertices() == 7); - Gudhi::Simplex_tree<> simplex_tree_60; BOOST_CHECK(alpha_complex_from_file.create_complex(simplex_tree_60, max_alpha_square_value)); std::cout << "simplex_tree_60.dimension()=" << simplex_tree_60.dimension() << std::endl; BOOST_CHECK(simplex_tree_60.dimension() == 2); - std::cout << "alpha_complex_from_points.number_of_vertices()=" << alpha_complex_from_file.number_of_vertices() - << std::endl; - BOOST_CHECK(alpha_complex_from_file.number_of_vertices() == 7); - std::cout << "simplex_tree_60.num_vertices()=" << simplex_tree_60.num_vertices() << std::endl; BOOST_CHECK(simplex_tree_60.num_vertices() == 7); @@ -128,10 +120,6 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_from_points) { Gudhi::Simplex_tree<> simplex_tree; BOOST_CHECK(alpha_complex_from_points.create_complex(simplex_tree)); - std::cout << "alpha_complex_from_points.number_of_vertices()=" << alpha_complex_from_points.number_of_vertices() - << std::endl; - BOOST_CHECK(alpha_complex_from_points.number_of_vertices() == points.size()); - // Another way to check num_simplices std::cout << "Iterator on alpha complex simplices in the filtration order, with [filtration value]:" << std::endl; int num_simplices = 0; @@ -151,7 +139,7 @@ BOOST_AUTO_TEST_CASE(Alpha_complex_from_points) { std::cout << "simplex_tree.dimension()=" << simplex_tree.dimension() << std::endl; BOOST_CHECK(simplex_tree.dimension() == 3); std::cout << "simplex_tree.num_vertices()=" << simplex_tree.num_vertices() << std::endl; - BOOST_CHECK(simplex_tree.num_vertices() == 4); + BOOST_CHECK(simplex_tree.num_vertices() == points.size()); for (auto f_simplex : simplex_tree.filtration_simplex_range()) { switch (simplex_tree.dimension(f_simplex)) { @@ -261,10 +249,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_empty_points, TestedKernel, lis Gudhi::Simplex_tree<> simplex_tree; BOOST_CHECK(!alpha_complex_from_points.create_complex(simplex_tree)); - std::cout << "alpha_complex_from_points.number_of_vertices()=" << alpha_complex_from_points.number_of_vertices() - << std::endl; - BOOST_CHECK(alpha_complex_from_points.number_of_vertices() == points.size()); - std::cout << "simplex_tree.num_simplices()=" << simplex_tree.num_simplices() << std::endl; BOOST_CHECK(simplex_tree.num_simplices() == 0); @@ -272,5 +256,45 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_empty_points, TestedKernel, lis BOOST_CHECK(simplex_tree.dimension() == -1); std::cout << "simplex_tree.num_vertices()=" << simplex_tree.num_vertices() << std::endl; - BOOST_CHECK(simplex_tree.num_vertices() == 0); + BOOST_CHECK(simplex_tree.num_vertices() == points.size()); +} + +using Inexact_kernel_2 = CGAL::Epick_d< CGAL::Dimension_tag<2> >; +using Exact_kernel_2 = CGAL::Epeck_d< CGAL::Dimension_tag<2> >; +using list_of_kernel_2_variants = boost::mpl::list; + +BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_with_duplicated_points, TestedKernel, list_of_kernel_2_variants) { + std::cout << "========== Alpha_complex_with_duplicated_points ==========" << std::endl; + + using Point = typename TestedKernel::Point_d; + using Vector_of_points = std::vector; + + // ---------------------------------------------------------------------------- + // Init of a list of points + // ---------------------------------------------------------------------------- + Vector_of_points points; + points.push_back(Point(1.0, 1.0)); + points.push_back(Point(7.0, 0.0)); + points.push_back(Point(4.0, 6.0)); + points.push_back(Point(9.0, 6.0)); + points.push_back(Point(0.0, 14.0)); + points.push_back(Point(2.0, 19.0)); + points.push_back(Point(9.0, 17.0)); + // duplicated points + points.push_back(Point(1.0, 1.0)); + points.push_back(Point(7.0, 0.0)); + + // ---------------------------------------------------------------------------- + // Init of an alpha complex from the list of points + // ---------------------------------------------------------------------------- + std::cout << "Init" << std::endl; + Gudhi::alpha_complex::Alpha_complex alpha_complex_from_points(points); + + Gudhi::Simplex_tree<> simplex_tree; + std::cout << "create_complex" << std::endl; + BOOST_CHECK(alpha_complex_from_points.create_complex(simplex_tree)); + + std::cout << "simplex_tree.num_vertices()=" << simplex_tree.num_vertices() + << std::endl; + BOOST_CHECK(simplex_tree.num_vertices() < points.size()); } -- cgit v1.2.3 From 94118b7c5c723bf62dcdafd404d492e8d78d0019 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 10 Dec 2019 10:06:00 +0100 Subject: Remove useless import --- src/python/doc/python3-sphinx-build.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py index d1f0f08e..84d158cf 100755 --- a/src/python/doc/python3-sphinx-build.py +++ b/src/python/doc/python3-sphinx-build.py @@ -5,7 +5,6 @@ Emulate sphinx-build for python3 """ from sys import exit, argv -import sphinx from sphinx import main if __name__ == '__main__': -- cgit v1.2.3 From ad0131187e70657b699eb205de901e50ab36a5d9 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 10 Dec 2019 11:44:21 +0100 Subject: Fix #118 for persistence_intervals --- .../include/gudhi/Persistence_intervals.h | 5 +++++ src/Persistence_representations/test/persistence_intervals_test.cpp | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Persistence_representations/include/gudhi/Persistence_intervals.h b/src/Persistence_representations/include/gudhi/Persistence_intervals.h index e2db4572..ea4220ea 100644 --- a/src/Persistence_representations/include/gudhi/Persistence_intervals.h +++ b/src/Persistence_representations/include/gudhi/Persistence_intervals.h @@ -6,6 +6,8 @@ * * Modification(s): * - YYYY/MM Author: Description of the modification + * - 2019/12 Vincent Rouvreau: Fix #118 - Make histogram_of_lengths and cumulative_histogram_of_lengths + * return the exact number_of_bins (was failing on x86) */ #ifndef PERSISTENCE_INTERVALS_H_ @@ -335,6 +337,9 @@ std::vector Persistence_intervals::histogram_of_lengths(size_t number_of getchar(); } } + // we want number of bins equals to number_of_bins (some unexpected results on x86) + result[number_of_bins-1]+=result[number_of_bins]; + result.resize(number_of_bins); if (dbg) { for (size_t i = 0; i != result.size(); ++i) std::cerr << result[i] << std::endl; diff --git a/src/Persistence_representations/test/persistence_intervals_test.cpp b/src/Persistence_representations/test/persistence_intervals_test.cpp index 3b7a2049..a29dcbee 100644 --- a/src/Persistence_representations/test/persistence_intervals_test.cpp +++ b/src/Persistence_representations/test/persistence_intervals_test.cpp @@ -85,8 +85,7 @@ BOOST_AUTO_TEST_CASE(check_histogram_of_lengths) { template_histogram.push_back(6); template_histogram.push_back(1); template_histogram.push_back(7); - template_histogram.push_back(1); - template_histogram.push_back(1); + template_histogram.push_back(2); for (size_t i = 0; i != histogram.size(); ++i) { BOOST_CHECK(histogram[i] == template_histogram[i]); } @@ -105,7 +104,6 @@ BOOST_AUTO_TEST_CASE(check_cumulative_histograms_of_lengths) { template_cumulative_histogram.push_back(35); template_cumulative_histogram.push_back(36); template_cumulative_histogram.push_back(43); - template_cumulative_histogram.push_back(44); template_cumulative_histogram.push_back(45); for (size_t i = 0; i != cumulative_histogram.size(); ++i) { -- cgit v1.2.3 From 291286815e282c9693f17615f8c46a49f87bf887 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 10 Dec 2019 13:33:02 +0100 Subject: Use initializer list instead of push_back to init vectors --- .../test/persistence_intervals_test.cpp | 209 ++++----------------- 1 file changed, 40 insertions(+), 169 deletions(-) diff --git a/src/Persistence_representations/test/persistence_intervals_test.cpp b/src/Persistence_representations/test/persistence_intervals_test.cpp index a29dcbee..02ea8edb 100644 --- a/src/Persistence_representations/test/persistence_intervals_test.cpp +++ b/src/Persistence_representations/test/persistence_intervals_test.cpp @@ -6,6 +6,8 @@ * * Modification(s): * - YYYY/MM Author: Description of the modification + * - 2019/12 Vincent Rouvreau: Fix #118 - Make histogram_of_lengths and cumulative_histogram_of_lengths + * return the exact number_of_bins (was failing on x86) */ #define BOOST_TEST_DYN_LINK @@ -32,17 +34,8 @@ BOOST_AUTO_TEST_CASE(check_min_max_function) { BOOST_AUTO_TEST_CASE(check_length_of_dominant_intervals) { Persistence_intervals p("data/file_with_diagram"); std::vector dominant_ten_intervals_length = p.length_of_dominant_intervals(10); - std::vector dominant_intervals_length; - dominant_intervals_length.push_back(0.862625); - dominant_intervals_length.push_back(0.800893); - dominant_intervals_length.push_back(0.762061); - dominant_intervals_length.push_back(0.756501); - dominant_intervals_length.push_back(0.729367); - dominant_intervals_length.push_back(0.718177); - dominant_intervals_length.push_back(0.708395); - dominant_intervals_length.push_back(0.702844); - dominant_intervals_length.push_back(0.700468); - dominant_intervals_length.push_back(0.622177); + std::vector dominant_intervals_length{0.862625, 0.800893, 0.762061, 0.756501, 0.729367, + 0.718177, 0.708395, 0.702844, 0.700468, 0.622177}; for (size_t i = 0; i != dominant_ten_intervals_length.size(); ++i) { GUDHI_TEST_FLOAT_EQUALITY_CHECK(dominant_ten_intervals_length[i], dominant_intervals_length[i], Gudhi::Persistence_representations::epsi); @@ -52,17 +45,11 @@ BOOST_AUTO_TEST_CASE(check_dominant_intervals) { Persistence_intervals p("data/file_with_diagram"); std::vector > ten_dominant_intervals = p.dominant_intervals(10); - std::vector > templ; - templ.push_back(std::pair(0.114718, 0.977343)); - templ.push_back(std::pair(0.133638, 0.93453)); - templ.push_back(std::pair(0.104599, 0.866659)); - templ.push_back(std::pair(0.149798, 0.906299)); - templ.push_back(std::pair(0.247352, 0.976719)); - templ.push_back(std::pair(0.192675, 0.910852)); - templ.push_back(std::pair(0.191836, 0.900231)); - templ.push_back(std::pair(0.284998, 0.987842)); - templ.push_back(std::pair(0.294069, 0.994537)); - templ.push_back(std::pair(0.267421, 0.889597)); + std::vector > templ{ {0.114718, 0.977343}, {0.133638, 0.93453}, + {0.104599, 0.866659}, {0.149798, 0.906299}, + {0.247352, 0.976719}, {0.192675, 0.910852}, + {0.191836, 0.900231}, {0.284998, 0.987842}, + {0.294069, 0.994537}, {0.267421, 0.889597} }; for (size_t i = 0; i != ten_dominant_intervals.size(); ++i) { GUDHI_TEST_FLOAT_EQUALITY_CHECK(ten_dominant_intervals[i].first, templ[i].first, @@ -75,17 +62,7 @@ BOOST_AUTO_TEST_CASE(check_dominant_intervals) { BOOST_AUTO_TEST_CASE(check_histogram_of_lengths) { Persistence_intervals p("data/file_with_diagram"); std::vector histogram = p.histogram_of_lengths(10); - std::vector template_histogram; - template_histogram.push_back(10); - template_histogram.push_back(5); - template_histogram.push_back(3); - template_histogram.push_back(4); - template_histogram.push_back(4); - template_histogram.push_back(3); - template_histogram.push_back(6); - template_histogram.push_back(1); - template_histogram.push_back(7); - template_histogram.push_back(2); + std::vector template_histogram{10, 5, 3, 4, 4, 3, 6, 1, 7, 2}; for (size_t i = 0; i != histogram.size(); ++i) { BOOST_CHECK(histogram[i] == template_histogram[i]); } @@ -94,17 +71,7 @@ BOOST_AUTO_TEST_CASE(check_histogram_of_lengths) { BOOST_AUTO_TEST_CASE(check_cumulative_histograms_of_lengths) { Persistence_intervals p("data/file_with_diagram"); std::vector cumulative_histogram = p.cumulative_histogram_of_lengths(10); - std::vector template_cumulative_histogram; - template_cumulative_histogram.push_back(10); - template_cumulative_histogram.push_back(15); - template_cumulative_histogram.push_back(18); - template_cumulative_histogram.push_back(22); - template_cumulative_histogram.push_back(26); - template_cumulative_histogram.push_back(29); - template_cumulative_histogram.push_back(35); - template_cumulative_histogram.push_back(36); - template_cumulative_histogram.push_back(43); - template_cumulative_histogram.push_back(45); + std::vector template_cumulative_histogram{10, 15, 18, 22, 26, 29, 35, 36, 43, 45}; for (size_t i = 0; i != cumulative_histogram.size(); ++i) { BOOST_CHECK(cumulative_histogram[i] == template_cumulative_histogram[i]); @@ -114,17 +81,8 @@ BOOST_AUTO_TEST_CASE(check_characteristic_function_of_diagram) { Persistence_intervals p("data/file_with_diagram"); std::pair min_max_ = p.get_x_range(); std::vector char_funct_diag = p.characteristic_function_of_diagram(min_max_.first, min_max_.second); - std::vector template_char_funct_diag; - template_char_funct_diag.push_back(0.370665); - template_char_funct_diag.push_back(0.84058); - template_char_funct_diag.push_back(1.24649); - template_char_funct_diag.push_back(1.3664); - template_char_funct_diag.push_back(1.34032); - template_char_funct_diag.push_back(1.31904); - template_char_funct_diag.push_back(1.14076); - template_char_funct_diag.push_back(0.991259); - template_char_funct_diag.push_back(0.800714); - template_char_funct_diag.push_back(0.0676303); + std::vector template_char_funct_diag{0.370665, 0.84058, 1.24649, 1.3664, 1.34032, + 1.31904, 1.14076, 0.991259, 0.800714, 0.0676303}; for (size_t i = 0; i != char_funct_diag.size(); ++i) { GUDHI_TEST_FLOAT_EQUALITY_CHECK(char_funct_diag[i], template_char_funct_diag[i], @@ -137,18 +95,8 @@ BOOST_AUTO_TEST_CASE(check_cumulative_characteristic_function_of_diagram) { std::pair min_max_ = p.get_x_range(); std::vector cumul_char_funct_diag = p.cumulative_characteristic_function_of_diagram(min_max_.first, min_max_.second); - std::vector template_char_funct_diag_cumul; - - template_char_funct_diag_cumul.push_back(0.370665); - template_char_funct_diag_cumul.push_back(1.21125); - template_char_funct_diag_cumul.push_back(2.45774); - template_char_funct_diag_cumul.push_back(3.82414); - template_char_funct_diag_cumul.push_back(5.16446); - template_char_funct_diag_cumul.push_back(6.4835); - template_char_funct_diag_cumul.push_back(7.62426); - template_char_funct_diag_cumul.push_back(8.61552); - template_char_funct_diag_cumul.push_back(9.41623); - template_char_funct_diag_cumul.push_back(9.48386); + std::vector template_char_funct_diag_cumul{0.370665, 1.21125, 2.45774, 3.82414, 5.16446, + 6.4835, 7.62426, 8.61552, 9.41623, 9.48386}; for (size_t i = 0; i != cumul_char_funct_diag.size(); ++i) { GUDHI_TEST_FLOAT_EQUALITY_CHECK(cumul_char_funct_diag[i], template_char_funct_diag_cumul[i], @@ -158,97 +106,29 @@ BOOST_AUTO_TEST_CASE(check_cumulative_characteristic_function_of_diagram) { BOOST_AUTO_TEST_CASE(check_compute_persistent_betti_numbers) { Persistence_intervals p("data/file_with_diagram"); - std::vector > pbns; - pbns.push_back(std::pair(0.0290362, 1)); - pbns.push_back(std::pair(0.0307676, 2)); - pbns.push_back(std::pair(0.0366312, 3)); - pbns.push_back(std::pair(0.0544614, 4)); - pbns.push_back(std::pair(0.0920033, 5)); - pbns.push_back(std::pair(0.104599, 6)); - pbns.push_back(std::pair(0.114718, 7)); - pbns.push_back(std::pair(0.117379, 8)); - pbns.push_back(std::pair(0.123493, 9)); - pbns.push_back(std::pair(0.133638, 10)); - pbns.push_back(std::pair(0.137798, 9)); - pbns.push_back(std::pair(0.149798, 10)); - pbns.push_back(std::pair(0.155421, 11)); - pbns.push_back(std::pair(0.158443, 12)); - pbns.push_back(std::pair(0.176956, 13)); - pbns.push_back(std::pair(0.183234, 12)); - pbns.push_back(std::pair(0.191069, 13)); - pbns.push_back(std::pair(0.191333, 14)); - pbns.push_back(std::pair(0.191836, 15)); - pbns.push_back(std::pair(0.192675, 16)); - pbns.push_back(std::pair(0.208564, 17)); - pbns.push_back(std::pair(0.218425, 18)); - pbns.push_back(std::pair(0.219902, 17)); - pbns.push_back(std::pair(0.23233, 16)); - pbns.push_back(std::pair(0.234558, 17)); - pbns.push_back(std::pair(0.237166, 16)); - pbns.push_back(std::pair(0.247352, 17)); - pbns.push_back(std::pair(0.267421, 18)); - pbns.push_back(std::pair(0.268093, 19)); - pbns.push_back(std::pair(0.278734, 18)); - pbns.push_back(std::pair(0.284722, 19)); - pbns.push_back(std::pair(0.284998, 20)); - pbns.push_back(std::pair(0.294069, 21)); - pbns.push_back(std::pair(0.306293, 22)); - pbns.push_back(std::pair(0.322361, 21)); - pbns.push_back(std::pair(0.323152, 22)); - pbns.push_back(std::pair(0.371021, 23)); - pbns.push_back(std::pair(0.372395, 24)); - pbns.push_back(std::pair(0.387744, 25)); - pbns.push_back(std::pair(0.435537, 26)); - pbns.push_back(std::pair(0.462911, 25)); - pbns.push_back(std::pair(0.483569, 26)); - pbns.push_back(std::pair(0.489209, 25)); - pbns.push_back(std::pair(0.517115, 24)); - pbns.push_back(std::pair(0.522197, 23)); - pbns.push_back(std::pair(0.532665, 22)); - pbns.push_back(std::pair(0.545262, 23)); - pbns.push_back(std::pair(0.587227, 22)); - pbns.push_back(std::pair(0.593036, 23)); - pbns.push_back(std::pair(0.602647, 24)); - pbns.push_back(std::pair(0.605044, 25)); - pbns.push_back(std::pair(0.621962, 24)); - pbns.push_back(std::pair(0.629449, 23)); - pbns.push_back(std::pair(0.636719, 22)); - pbns.push_back(std::pair(0.64957, 21)); - pbns.push_back(std::pair(0.650781, 22)); - pbns.push_back(std::pair(0.654951, 23)); - pbns.push_back(std::pair(0.683489, 24)); - pbns.push_back(std::pair(0.687172, 23)); - pbns.push_back(std::pair(0.69703, 22)); - pbns.push_back(std::pair(0.701174, 21)); - pbns.push_back(std::pair(0.717623, 22)); - pbns.push_back(std::pair(0.722023, 21)); - pbns.push_back(std::pair(0.722298, 20)); - pbns.push_back(std::pair(0.725347, 19)); - pbns.push_back(std::pair(0.73071, 18)); - pbns.push_back(std::pair(0.758355, 17)); - pbns.push_back(std::pair(0.770913, 18)); - pbns.push_back(std::pair(0.790833, 17)); - pbns.push_back(std::pair(0.821211, 16)); - pbns.push_back(std::pair(0.849305, 17)); - pbns.push_back(std::pair(0.853669, 16)); - pbns.push_back(std::pair(0.866659, 15)); - pbns.push_back(std::pair(0.872896, 16)); - pbns.push_back(std::pair(0.889597, 15)); - pbns.push_back(std::pair(0.900231, 14)); - pbns.push_back(std::pair(0.903847, 13)); - pbns.push_back(std::pair(0.906299, 12)); - pbns.push_back(std::pair(0.910852, 11)); - pbns.push_back(std::pair(0.93453, 10)); - pbns.push_back(std::pair(0.944757, 9)); - pbns.push_back(std::pair(0.947812, 8)); - pbns.push_back(std::pair(0.959154, 7)); - pbns.push_back(std::pair(0.975654, 6)); - pbns.push_back(std::pair(0.976719, 5)); - pbns.push_back(std::pair(0.977343, 4)); - pbns.push_back(std::pair(0.980129, 3)); - pbns.push_back(std::pair(0.987842, 2)); - pbns.push_back(std::pair(0.990127, 1)); - pbns.push_back(std::pair(0.994537, 0)); + std::vector > pbns{ {0.0290362, 1}, {0.0307676, 2}, {0.0366312, 3}, {0.0544614, 4}, + {0.0920033, 5}, {0.104599, 6}, {0.114718, 7}, {0.117379, 8}, + {0.123493, 9}, {0.133638, 10}, {0.137798, 9}, {0.149798, 10}, + {0.155421, 11}, {0.158443, 12}, {0.176956, 13}, {0.183234, 12}, + {0.191069, 13}, {0.191333, 14}, {0.191836, 15}, {0.192675, 16}, + {0.208564, 17}, {0.218425, 18}, {0.219902, 17}, {0.23233, 16}, + {0.234558, 17}, {0.237166, 16}, {0.247352, 17}, {0.267421, 18}, + {0.268093, 19}, {0.278734, 18}, {0.284722, 19}, {0.284998, 20}, + {0.294069, 21}, {0.306293, 22}, {0.322361, 21}, {0.323152, 22}, + {0.371021, 23}, {0.372395, 24}, {0.387744, 25}, {0.435537, 26}, + {0.462911, 25}, {0.483569, 26}, {0.489209, 25}, {0.517115, 24}, + {0.522197, 23}, {0.532665, 22}, {0.545262, 23}, {0.587227, 22}, + {0.593036, 23}, {0.602647, 24}, {0.605044, 25}, {0.621962, 24}, + {0.629449, 23}, {0.636719, 22}, {0.64957, 21}, {0.650781, 22}, + {0.654951, 23}, {0.683489, 24}, {0.687172, 23}, {0.69703, 22}, + {0.701174, 21}, {0.717623, 22}, {0.722023, 21}, {0.722298, 20}, + {0.725347, 19}, {0.73071, 18}, {0.758355, 17}, {0.770913, 18}, + {0.790833, 17}, {0.821211, 16}, {0.849305, 17}, {0.853669, 16}, + {0.866659, 15}, {0.872896, 16}, {0.889597, 15}, {0.900231, 14}, + {0.903847, 13}, {0.906299, 12}, {0.910852, 11}, {0.93453, 10}, + {0.944757, 9}, {0.947812, 8}, {0.959154, 7}, {0.975654, 6}, + {0.976719, 5}, {0.977343, 4}, {0.980129, 3}, {0.987842, 2}, + {0.990127, 1}, {0.994537, 0} }; std::vector > pbns_new = p.compute_persistent_betti_numbers(); for (size_t i = 0; i != pbns.size(); ++i) { @@ -260,17 +140,8 @@ BOOST_AUTO_TEST_CASE(check_compute_persistent_betti_numbers) { BOOST_AUTO_TEST_CASE(check_k_n_n) { Persistence_intervals p("data/file_with_diagram"); std::vector knn = p.k_n_n(5); - std::vector knn_template; - knn_template.push_back(1.04208); - knn_template.push_back(1.00344); - knn_template.push_back(0.979395); - knn_template.push_back(0.890643); - knn_template.push_back(0.874769); - knn_template.push_back(0.845787); - knn_template.push_back(0.819713); - knn_template.push_back(0.803984); - knn_template.push_back(0.799864); - knn_template.push_back(0.786945); + std::vector knn_template{1.04208, 1.00344, 0.979395, 0.890643, 0.874769, + 0.845787, 0.819713, 0.803984, 0.799864, 0.786945}; for (size_t i = 0; i != knn.size(); ++i) { GUDHI_TEST_FLOAT_EQUALITY_CHECK(knn[i], knn_template[i], Gudhi::Persistence_representations::epsi); -- cgit v1.2.3 From 5ecc15ba30e7a20604d50c1fdec9e7da2de64898 Mon Sep 17 00:00:00 2001 From: mathieu Date: Tue, 10 Dec 2019 14:24:52 -0500 Subject: fixed doc and examples --- src/python/doc/representations.rst | 4 ++-- src/python/example/diagram_vectorizations_distances_kernels.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index b338f7f0..409e97da 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -10,7 +10,7 @@ Representations manual This module, originally available at https://github.com/MathieuCarriere/sklearn-tda and named sklearn_tda, aims at bridging the gap between persistence diagrams and machine learning, by providing implementations of most of the vector representations for persistence diagrams in the literature, in a scikit-learn format. More specifically, it provides tools, using the scikit-learn standard interface, to compute distances and kernels on persistence diagrams, and to convert these diagrams into vectors in Euclidean space. -A diagram is represented as a numpy array of shape (n,2), as can be obtained from `SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. +A diagram is represented as a numpy array of shape (n,2), as can be obtained from :func:`~gudhi.SimplexTree.persistence_intervals_in_dimension` for instance. Points at infinity are represented as a numpy array of shape (n,1), storing only the birth time. A small example is provided @@ -66,4 +66,4 @@ The output is: .. testoutput:: - [[0. 1.25707872 2.51415744 1.88561808 0.7856742 2.04275292 3.29983165 2.51415744 1.25707872 0. 0. 0. 0.31426968 0. 0.62853936 0. 0. 0.31426968 1.25707872 0. ]] + [[1.02851895 2.05703791 2.57129739 1.54277843 0.89995409 1.92847304 2.95699199 3.08555686 0. 0.64282435 0. 0. 0.51425948 0. 0. 0. ]] diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py index f777984c..0ea4ba79 100755 --- a/src/python/example/diagram_vectorizations_distances_kernels.py +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -26,9 +26,9 @@ plt.show() LS = Landscape(resolution=1000) L = LS.fit_transform(diags) -plt.plot(L[0][:999]) -plt.plot(L[0][999:2*999]) -plt.plot(L[0][2*999:3*999]) +plt.plot(L[0][:998]) +plt.plot(L[0][998:2*998]) +plt.plot(L[0][2*998:3*998]) plt.title("Landscape") plt.show() -- cgit v1.2.3 From 0474f6a62622608b96eac3a553a081e148cbcabc Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 10 Dec 2019 22:51:08 +0100 Subject: Remove FindMPFR mechanism as the job is already done by CGAL --- src/cmake/modules/FindMPFR.cmake | 51 ---------------------- .../modules/GUDHI_third_party_libraries.cmake | 2 - src/python/CMakeLists.txt | 44 ++++++++----------- 3 files changed, 19 insertions(+), 78 deletions(-) delete mode 100644 src/cmake/modules/FindMPFR.cmake diff --git a/src/cmake/modules/FindMPFR.cmake b/src/cmake/modules/FindMPFR.cmake deleted file mode 100644 index 6c963272..00000000 --- a/src/cmake/modules/FindMPFR.cmake +++ /dev/null @@ -1,51 +0,0 @@ -# Try to find the MPFR libraries -# MPFR_FOUND - system has MPFR lib -# MPFR_INCLUDE_DIR - the MPFR include directory -# MPFR_LIBRARIES_DIR - Directory where the MPFR libraries are located -# MPFR_LIBRARIES - the MPFR libraries - -# TODO: support MacOSX - -include(FindPackageHandleStandardArgs) - -if(MPFR_INCLUDE_DIR) - set(MPFR_in_cache TRUE) -else() - set(MPFR_in_cache FALSE) -endif() -if(NOT MPFR_LIBRARIES) - set(MPFR_in_cache FALSE) -endif() - -# Is it already configured? -if (MPFR_in_cache) - set(MPFR_FOUND TRUE) -else() - find_path(MPFR_INCLUDE_DIR - NAMES mpfr.h - HINTS ENV MPFR_INC_DIR - ENV MPFR_DIR - ${CGAL_INSTALLATION_PACKAGE_DIR}/auxiliary/gmp/include - PATH_SUFFIXES include - DOC "The directory containing the MPFR header files" - ) - - find_library(MPFR_LIBRARIES NAMES mpfr libmpfr-4 libmpfr-1 - HINTS ENV MPFR_LIB_DIR - ENV MPFR_DIR - ${CGAL_INSTALLATION_PACKAGE_DIR}/auxiliary/gmp/lib - PATH_SUFFIXES lib - DOC "Path to the MPFR library" - ) - - if ( MPFR_LIBRARIES ) - get_filename_component(MPFR_LIBRARIES_DIR ${MPFR_LIBRARIES} PATH CACHE ) - endif() - - # Attempt to load a user-defined configuration for MPFR if couldn't be found - if ( NOT MPFR_INCLUDE_DIR OR NOT MPFR_LIBRARIES_DIR ) - include( MPFRConfig OPTIONAL ) - endif() - - find_package_handle_standard_args(MPFR "DEFAULT_MSG" MPFR_LIBRARIES MPFR_INCLUDE_DIR) -endif() diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index d8c7a428..24a34150 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -6,8 +6,6 @@ if(NOT Boost_FOUND) message(FATAL_ERROR "NOTICE: This program requires Boost and will not be compiled.") endif(NOT Boost_FOUND) -find_package(MPFR) - find_package(GMP) if(GMP_FOUND) INCLUDE_DIRECTORIES(${GMP_INCLUDE_DIR}) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 13a8a909..1704f491 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -230,14 +230,12 @@ endif(CGAL_FOUND) # Test examples if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - if (MPFR_FOUND) - # Bottleneck and Alpha - add_test(NAME alpha_rips_persistence_bottleneck_distance_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_rips_persistence_bottleneck_distance.py" - -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -t 0.15 -d 3) - endif(MPFR_FOUND) + # Bottleneck and Alpha + add_test(NAME alpha_rips_persistence_bottleneck_distance_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_rips_persistence_bottleneck_distance.py" + -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -t 0.15 -d 3) if(MATPLOTLIB_FOUND AND NUMPY_FOUND) # Tangential add_test(NAME tangential_complex_plain_homology_from_off_file_example_py_test @@ -308,23 +306,19 @@ endif(CGAL_FOUND) endif (NOT CGAL_VERSION VERSION_LESS 4.11.0) if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) - if (MPFR_FOUND) - # Alpha - add_test(NAME alpha_complex_from_points_example_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_from_points_example.py") - - if(MATPLOTLIB_FOUND AND NUMPY_FOUND) - add_test(NAME alpha_complex_diagram_persistence_from_off_file_example_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_diagram_persistence_from_off_file_example.py" - --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 0.6) - endif() - - add_gudhi_py_test(test_alpha_complex) - endif(MPFR_FOUND) + # Alpha + add_test(NAME alpha_complex_from_points_example_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_from_points_example.py") + if(MATPLOTLIB_FOUND AND NUMPY_FOUND) + add_test(NAME alpha_complex_diagram_persistence_from_off_file_example_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_diagram_persistence_from_off_file_example.py" + --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 0.6) + endif() + add_gudhi_py_test(test_alpha_complex) endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) -- cgit v1.2.3 From 682f8c8cb18ba898a3d23a82fff454e862541aed Mon Sep 17 00:00:00 2001 From: Mathieu Carrière Date: Wed, 11 Dec 2019 13:48:26 -0500 Subject: Update src/python/doc/representations.rst Co-Authored-By: Vincent Rouvreau <10407034+VincentRouvreau@users.noreply.github.com> --- src/python/doc/representations.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index 409e97da..470b57bf 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -66,4 +66,6 @@ The output is: .. testoutput:: - [[1.02851895 2.05703791 2.57129739 1.54277843 0.89995409 1.92847304 2.95699199 3.08555686 0. 0.64282435 0. 0. 0.51425948 0. 0. 0. ]] + [[1.02851895 2.05703791 2.57129739 1.54277843 0.89995409 1.92847304 + 2.95699199 3.08555686 0. 0.64282435 0. 0. + 0.51425948 0. 0. 0. ]] -- cgit v1.2.3 From 363ae171ee7f45cf11d01653e4d4e9580117cfd0 Mon Sep 17 00:00:00 2001 From: mathieu Date: Wed, 11 Dec 2019 13:50:21 -0500 Subject: fixed landscape --- .../example/diagram_vectorizations_distances_kernels.py | 6 +++--- src/python/gudhi/representations/vector_methods.py | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/python/example/diagram_vectorizations_distances_kernels.py b/src/python/example/diagram_vectorizations_distances_kernels.py index 0ea4ba79..119072eb 100755 --- a/src/python/example/diagram_vectorizations_distances_kernels.py +++ b/src/python/example/diagram_vectorizations_distances_kernels.py @@ -26,9 +26,9 @@ plt.show() LS = Landscape(resolution=1000) L = LS.fit_transform(diags) -plt.plot(L[0][:998]) -plt.plot(L[0][998:2*998]) -plt.plot(L[0][2*998:3*998]) +plt.plot(L[0][:1000]) +plt.plot(L[0][1000:2000]) +plt.plot(L[0][2000:3000]) plt.title("Landscape") plt.show() diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py index 083551a4..cd532275 100644 --- a/src/python/gudhi/representations/vector_methods.py +++ b/src/python/gudhi/representations/vector_methods.py @@ -129,19 +129,19 @@ class Landscape(BaseEstimator, TransformerMixin): diagram, num_pts_in_diag = X[i], X[i].shape[0] - ls = np.zeros([self.num_landscapes, self.resolution]) + ls = np.zeros([self.num_landscapes, self.resolution + self.nan_in_range.sum()]) events = [] - for j in range(self.resolution): + for j in range(self.resolution + self.nan_in_range.sum()): events.append([]) for j in range(num_pts_in_diag): [px,py] = diagram[j,:2] - min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) - mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) - max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) + mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) - if min_idx < self.resolution and max_idx > 0: + if min_idx < self.resolution + self.nan_in_range.sum() and max_idx > 0: landscape_value = self.sample_range[0] + min_idx * step_x - px for k in range(min_idx, mid_idx): @@ -153,7 +153,7 @@ class Landscape(BaseEstimator, TransformerMixin): events[k].append(landscape_value) landscape_value -= step_x - for j in range(self.resolution): + for j in range(self.resolution + self.nan_in_range.sum()): events[j].sort(reverse=True) for k in range( min(self.num_landscapes, len(events[j])) ): ls[k,j] = events[j][k] -- cgit v1.2.3 From 9e75cc1832403f8ffec38fc3a4f6b1081fe4770e Mon Sep 17 00:00:00 2001 From: mathieu Date: Wed, 11 Dec 2019 13:57:15 -0500 Subject: update example --- src/python/doc/representations.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/python/doc/representations.rst b/src/python/doc/representations.rst index 470b57bf..11dcbcf9 100644 --- a/src/python/doc/representations.rst +++ b/src/python/doc/representations.rst @@ -67,5 +67,6 @@ The output is: .. testoutput:: [[1.02851895 2.05703791 2.57129739 1.54277843 0.89995409 1.92847304 - 2.95699199 3.08555686 0. 0.64282435 0. 0. - 0.51425948 0. 0. 0. ]] + 2.95699199 3.08555686 2.05703791 1.02851895 0. 0.64282435 + 0. 0. 0.51425948 0. 0. 0. + 0.77138922 1.02851895]] -- cgit v1.2.3 From 2886885ff4cf1f134863de0fa97b64f824d67622 Mon Sep 17 00:00:00 2001 From: mathieu Date: Wed, 11 Dec 2019 15:30:45 -0500 Subject: cleanup --- src/python/gudhi/representations/vector_methods.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py index cd532275..9b280f68 100644 --- a/src/python/gudhi/representations/vector_methods.py +++ b/src/python/gudhi/representations/vector_methods.py @@ -95,6 +95,7 @@ class Landscape(BaseEstimator, TransformerMixin): sample_range ([double, double]): minimum and maximum of all piecewise-linear function domains, of the form [x_min, x_max] (default [numpy.nan, numpy.nan]). It is the interval on which samples will be drawn evenly. If one of the values is numpy.nan, it can be computed from the persistence diagrams with the fit() method. """ self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range + self.nan_in_range = np.isnan(np.array(self.sample_range)) def fit(self, X, y=None): """ @@ -104,8 +105,7 @@ class Landscape(BaseEstimator, TransformerMixin): X (list of n x 2 numpy arrays): input persistence diagrams. y (n x 1 array): persistence diagram labels (unused). """ - self.nan_in_range = np.isnan(np.array(self.sample_range)) - if np.isnan(np.array(self.sample_range)).any(): + if self.nan_in_range.any(): pre = DiagramScaler(use=True, scalers=[([0], MinMaxScaler()), ([1], MinMaxScaler())]).fit(X,y) [mx,my],[Mx,My] = [pre.scalers[0][1].data_min_[0], pre.scalers[1][1].data_min_[0]], [pre.scalers[0][1].data_max_[0], pre.scalers[1][1].data_max_[0]] self.sample_range = np.where(self.nan_in_range, np.array([mx, My]), np.array(self.sample_range)) -- cgit v1.2.3 From 7bd6907e577e22803fec179f652ecf0ec64dcb4a Mon Sep 17 00:00:00 2001 From: mathieu Date: Wed, 11 Dec 2019 15:38:00 -0500 Subject: cleanup for landscape resolution --- src/python/gudhi/representations/vector_methods.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/python/gudhi/representations/vector_methods.py b/src/python/gudhi/representations/vector_methods.py index 9b280f68..fe26dbe2 100644 --- a/src/python/gudhi/representations/vector_methods.py +++ b/src/python/gudhi/representations/vector_methods.py @@ -96,6 +96,7 @@ class Landscape(BaseEstimator, TransformerMixin): """ self.num_landscapes, self.resolution, self.sample_range = num_landscapes, resolution, sample_range self.nan_in_range = np.isnan(np.array(self.sample_range)) + self.new_resolution = self.resolution + self.nan_in_range.sum() def fit(self, X, y=None): """ @@ -122,26 +123,26 @@ class Landscape(BaseEstimator, TransformerMixin): numpy array with shape (number of diagrams) x (number of samples = **num_landscapes** x **resolution**): output persistence landscapes. """ num_diag, Xfit = len(X), [] - x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.resolution + self.nan_in_range.sum()) + x_values = np.linspace(self.sample_range[0], self.sample_range[1], self.new_resolution) step_x = x_values[1] - x_values[0] for i in range(num_diag): diagram, num_pts_in_diag = X[i], X[i].shape[0] - ls = np.zeros([self.num_landscapes, self.resolution + self.nan_in_range.sum()]) + ls = np.zeros([self.num_landscapes, self.new_resolution]) events = [] - for j in range(self.resolution + self.nan_in_range.sum()): + for j in range(self.new_resolution): events.append([]) for j in range(num_pts_in_diag): [px,py] = diagram[j,:2] - min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) - mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) - max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.resolution + self.nan_in_range.sum()) + min_idx = np.clip(np.ceil((px - self.sample_range[0]) / step_x).astype(int), 0, self.new_resolution) + mid_idx = np.clip(np.ceil((0.5*(py+px) - self.sample_range[0]) / step_x).astype(int), 0, self.new_resolution) + max_idx = np.clip(np.ceil((py - self.sample_range[0]) / step_x).astype(int), 0, self.new_resolution) - if min_idx < self.resolution + self.nan_in_range.sum() and max_idx > 0: + if min_idx < self.new_resolution and max_idx > 0: landscape_value = self.sample_range[0] + min_idx * step_x - px for k in range(min_idx, mid_idx): @@ -153,7 +154,7 @@ class Landscape(BaseEstimator, TransformerMixin): events[k].append(landscape_value) landscape_value -= step_x - for j in range(self.resolution + self.nan_in_range.sum()): + for j in range(self.new_resolution): events[j].sort(reverse=True) for k in range( min(self.num_landscapes, len(events[j])) ): ls[k,j] = events[j][k] -- cgit v1.2.3 From 7f426f5a85051676ffb6f8952689bd400ddcc10b Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau <10407034+VincentRouvreau@users.noreply.github.com> Date: Thu, 12 Dec 2019 09:40:35 +0100 Subject: Code review: Typo in src/common/doc/installation.h Co-Authored-By: Marc Glisse --- src/common/doc/installation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index c4ca10d1..d70a2efa 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -34,7 +34,7 @@ make \endverbatim * To test your build, run the following command in a terminal: * \verbatim make test \endverbatim * `make test` is using Ctest<\a> (CMake test driver - * program). If some of the tests are failing, plend send us the result of the following command: + * program). If some of the tests are failing, please send us the result of the following command: * \verbatim ctest --output-on-failure \endverbatim * * \subsection documentationgeneration Documentation -- cgit v1.2.3 From a1b6e5a034bc698b02d3c93e1707cab5622eaea6 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau <10407034+VincentRouvreau@users.noreply.github.com> Date: Thu, 12 Dec 2019 09:41:04 +0100 Subject: Code review: typo in src/python/doc/installation.rst Co-Authored-By: Marc Glisse --- src/python/doc/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 3553ae51..c3ddc017 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -100,7 +100,7 @@ following command in a terminal: `make test` is using `Ctest `_ (CMake test -driver program). If some of the tests are failing, plend send us the result of +driver program). If some of the tests are failing, please send us the result of the following command: .. code-block:: bash -- cgit v1.2.3 From 38f0ff98cf6da1e882b6b44fc3ed7b3d310fb91f Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 12 Dec 2019 10:00:10 +0100 Subject: Doc review: use ctest directly and provide options suggestions --- src/python/doc/installation.rst | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 3553ae51..50a697c7 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -83,32 +83,30 @@ Or install it definitely in your Python packages folder: .. code-block:: bash - python setup.py install --prefix /home/gudhi # Install in /home/gudhi directory + python setup.py install --prefix /home/gudhi # Install in /home/gudhi directory Test suites =========== -To test your build, `py.test `_ is optional. Run the -following command in a terminal: +To test your build, `py.test `_ is required. Run the +following `Ctest `_ +(CMake test driver program) command in a terminal: .. code-block:: bash cd /path-to-gudhi/build/python # For windows, you have to set PYTHONPATH environment variable export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/python' - make test + ctest -`make test` is using -`Ctest `_ (CMake test -driver program). If some of the tests are failing, plend send us the result of -the following command: +.. note:: + + One can use :code:`ctest` specific options in the python directory: .. code-block:: bash - cd /path-to-gudhi/build/python - # For windows, you have to set PYTHONPATH environment variable - export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/python' - ctest --output-on-failure + # Launch tests in parallel on 8 cores and set failing tests in verbose mode + ctest -j 8 --output-on-failure Debugging issues ================ -- cgit v1.2.3 From b884a620824a9d707cec82c9dddf7ab236263b95 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 12 Dec 2019 10:04:59 +0100 Subject: conflict resolution --- src/python/doc/installation.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 14000ecf..50a697c7 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -99,16 +99,9 @@ following `Ctest `_ export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/python' ctest -<<<<<<< HEAD .. note:: One can use :code:`ctest` specific options in the python directory: -======= -`make test` is using -`Ctest `_ (CMake test -driver program). If some of the tests are failing, please send us the result of -the following command: ->>>>>>> a1b6e5a034bc698b02d3c93e1707cab5622eaea6 .. code-block:: bash -- cgit v1.2.3 From 3f2eb763d4cdbf895a95c89ee938940962a9d938 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 13 Dec 2019 11:32:38 +0100 Subject: Add a more representative image for python persistence representations --- src/Persistence_representations/doc/sklearn-tda.png | Bin 0 -> 388075 bytes src/python/doc/representations_sum.inc | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 src/Persistence_representations/doc/sklearn-tda.png diff --git a/src/Persistence_representations/doc/sklearn-tda.png b/src/Persistence_representations/doc/sklearn-tda.png new file mode 100644 index 00000000..f0ff07f4 Binary files /dev/null and b/src/Persistence_representations/doc/sklearn-tda.png differ diff --git a/src/python/doc/representations_sum.inc b/src/python/doc/representations_sum.inc index 7b167a17..d4fe37f6 100644 --- a/src/python/doc/representations_sum.inc +++ b/src/python/doc/representations_sum.inc @@ -3,7 +3,7 @@ +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ | .. figure:: | Vectorizations, distances and kernels that work on persistence | :Author: Mathieu Carrière | - | ../../doc/Persistence_representations/average_landscape.png | diagrams, compatible with scikit-learn. | | + | ../../doc/Persistence_representations/sklearn-tda.png | diagrams, compatible with scikit-learn. | | | | | :Introduced in: GUDHI 3.1.0 | | | | | | | | :Copyright: MIT | -- cgit v1.2.3 From 03c577e3b2b4cea844d64c7aeb68b9f73db4602a Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 13 Dec 2019 12:05:06 +0100 Subject: Move image in python img instead of C++ one --- src/Persistence_representations/doc/sklearn-tda.png | Bin 388075 -> 0 bytes src/python/doc/img/sklearn-tda.png | Bin 0 -> 388075 bytes src/python/doc/representations_sum.inc | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 src/Persistence_representations/doc/sklearn-tda.png create mode 100644 src/python/doc/img/sklearn-tda.png diff --git a/src/Persistence_representations/doc/sklearn-tda.png b/src/Persistence_representations/doc/sklearn-tda.png deleted file mode 100644 index f0ff07f4..00000000 Binary files a/src/Persistence_representations/doc/sklearn-tda.png and /dev/null differ diff --git a/src/python/doc/img/sklearn-tda.png b/src/python/doc/img/sklearn-tda.png new file mode 100644 index 00000000..f0ff07f4 Binary files /dev/null and b/src/python/doc/img/sklearn-tda.png differ diff --git a/src/python/doc/representations_sum.inc b/src/python/doc/representations_sum.inc index d4fe37f6..700828f1 100644 --- a/src/python/doc/representations_sum.inc +++ b/src/python/doc/representations_sum.inc @@ -3,7 +3,7 @@ +------------------------------------------------------------------+----------------------------------------------------------------+-----------------------------------------------+ | .. figure:: | Vectorizations, distances and kernels that work on persistence | :Author: Mathieu Carrière | - | ../../doc/Persistence_representations/sklearn-tda.png | diagrams, compatible with scikit-learn. | | + | img/sklearn-tda.png | diagrams, compatible with scikit-learn. | | | | | :Introduced in: GUDHI 3.1.0 | | | | | | | | :Copyright: MIT | -- cgit v1.2.3 From 7b2ac5ccd53ec8988854e5ac2c3c20176e0c24ed Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 16 Dec 2019 10:47:34 +0100 Subject: Fix MPFR when CGAL is not header only --- src/python/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 1704f491..b558d4c4 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -169,6 +169,10 @@ if(PYTHONINTERP_FOUND) add_gudhi_debug_info("MPFR_LIBRARIES = ${MPFR_LIBRARIES}") set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_USE_MPFR', ") add_GUDHI_PYTHON_lib("${MPFR_LIBRARIES}") + # In case CGAL is not header only, all MPFR variables are set except MPFR_LIBRARIES_DIR - Just set it + if(NOT MPFR_LIBRARIES_DIR) + get_filename_component(MPFR_LIBRARIES_DIR ${MPFR_LIBRARIES} PATH) + endif(NOT MPFR_LIBRARIES_DIR) set(GUDHI_PYTHON_LIBRARY_DIRS "${GUDHI_PYTHON_LIBRARY_DIRS}'${MPFR_LIBRARIES_DIR}', ") message("** Add mpfr ${MPFR_LIBRARIES}") endif(MPFR_FOUND) -- cgit v1.2.3 From 490a5774601e344bb732de5eab2a8cf6d5a7e81f Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 16 Dec 2019 13:38:58 +0100 Subject: Rollback exact version mechanism --- src/python/gudhi/alpha_complex.pyx | 11 +++-------- src/python/include/Alpha_complex_interface.h | 4 ++-- src/python/test/test_alpha_complex.py | 12 +++--------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index bfb9783a..24e36bea 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -28,7 +28,7 @@ cdef extern from "Alpha_complex_interface.h" namespace "Gudhi": # bool from_file is a workaround for cython to find the correct signature Alpha_complex_interface(string off_file, bool from_file) vector[double] get_point(int vertex) - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square, bool exact_version) + void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square) # AlphaComplex python interface cdef class AlphaComplex: @@ -99,22 +99,17 @@ cdef class AlphaComplex: cdef vector[double] point = self.thisptr.get_point(vertex) return point - def create_simplex_tree(self, max_alpha_square = float('inf'), - exact_version = False): + def create_simplex_tree(self, max_alpha_square = float('inf')): """ :param max_alpha_square: The maximum alpha square threshold the simplices shall not exceed. Default is set to infinity, and there is very little point using anything else since it does not save time. :type max_alpha_square: float - :param exact_version: :code:`EXACT` computation version if set. - Default is false which means :code:`SAFE` version is used. - :type exact_version: bool :returns: A simplex tree created from the Delaunay Triangulation. :rtype: SimplexTree """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - self.thisptr.create_simplex_tree(stree_int_ptr, - max_alpha_square, exact_version) + self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square) return stree diff --git a/src/python/include/Alpha_complex_interface.h b/src/python/include/Alpha_complex_interface.h index a7621f2b..e9bbadb0 100644 --- a/src/python/include/Alpha_complex_interface.h +++ b/src/python/include/Alpha_complex_interface.h @@ -60,8 +60,8 @@ class Alpha_complex_interface { return vd; } - void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, bool exact_version) { - alpha_complex_->create_complex(*simplex_tree, max_alpha_square, exact_version); + void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square) { + alpha_complex_->create_complex(*simplex_tree, max_alpha_square); simplex_tree->initialize_filtration(); } diff --git a/src/python/test/test_alpha_complex.py b/src/python/test/test_alpha_complex.py index ab84daaa..9b27fff2 100755 --- a/src/python/test/test_alpha_complex.py +++ b/src/python/test/test_alpha_complex.py @@ -93,7 +93,7 @@ def test_filtered_alpha(): assert simplex_tree.get_star([0]) == [([0], 0.0), ([0, 1], 0.25), ([0, 2], 0.25)] assert simplex_tree.get_cofaces([0], 1) == [([0, 1], 0.25), ([0, 2], 0.25)] -def alpha_persistence_comparison(exact_version): +def test_safe_alpha_persistence_comparison(): #generate periodic signal time = np.arange(0, 10, 1) signal = [math.sin(x) for x in time] @@ -106,10 +106,10 @@ def alpha_persistence_comparison(exact_version): #build alpha complex and simplex tree alpha_complex1 = AlphaComplex(points=embedding1) - simplex_tree1 = alpha_complex1.create_simplex_tree(exact_version = exact_version) + simplex_tree1 = alpha_complex1.create_simplex_tree() alpha_complex2 = AlphaComplex(points=embedding2) - simplex_tree2 = alpha_complex2.create_simplex_tree(exact_version = exact_version) + simplex_tree2 = alpha_complex2.create_simplex_tree() diag1 = simplex_tree1.persistence() diag2 = simplex_tree2.persistence() @@ -117,9 +117,3 @@ def alpha_persistence_comparison(exact_version): for (first_p, second_p) in itertools.zip_longest(diag1, diag2): assert first_p[0] == pytest.approx(second_p[0]) assert first_p[1] == pytest.approx(second_p[1]) - -def test_exact_alpha_version(): - alpha_persistence_comparison(exact_version = True) - -def test_safe_alpha_version(): - alpha_persistence_comparison(exact_version = False) -- cgit v1.2.3 From b5510c0c362cd2bef79d82fd9809e654aea73957 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 16 Dec 2019 23:10:24 +0100 Subject: Add numpy ndarray to init a cubical and a periodic one --- src/python/doc/cubical_complex_user.rst | 4 +-- src/python/gudhi/cubical_complex.pyx | 27 ++++++++++++++----- src/python/gudhi/periodic_cubical_complex.pyx | 39 ++++++++++++++++++++++----- src/python/test/test_cubical_complex.py | 33 ++++++++++++++++++++--- 4 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/python/doc/cubical_complex_user.rst b/src/python/doc/cubical_complex_user.rst index b13b500e..21806038 100644 --- a/src/python/doc/cubical_complex_user.rst +++ b/src/python/doc/cubical_complex_user.rst @@ -142,8 +142,8 @@ Or it can be defined as follows: .. testcode:: from gudhi import PeriodicCubicalComplex as pcc - periodic_cc = pcc(dimensions=[3,3], - top_dimensional_cells= [0, 0, 0, 0, 1, 0, 0, 0, 0], + from numpy import array as np_array + periodic_cc = pcc(numpy_array = np_array([[0, 0, 0], [0, 1, 0], [0, 0, 0]]), periodic_dimensions=[True, False]) result_str = 'Periodic cubical complex is of dimension ' + repr(periodic_cc.dimension()) + ' - ' + \ repr(periodic_cc.num_simplices()) + ' simplices.' diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 011c407c..2ec0146a 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -5,7 +5,7 @@ from libcpp.string cimport string from libcpp cimport bool import os -from numpy import array as np_array +import numpy as np # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -47,7 +47,7 @@ cdef class CubicalComplex: # Fake constructor that does nothing but documenting the constructor def __init__(self, dimensions=None, top_dimensional_cells=None, - perseus_file=''): + numpy_array=None, perseus_file=''): """CubicalComplex constructor from dimensions and top_dimensional_cells or from a Perseus-style file name. @@ -58,16 +58,31 @@ cdef class CubicalComplex: Or + :param numpy_array: Filtration values in a d-array. + :type numpy_array: numpy ndarray + + Or + :param perseus_file: A Perseus-style file name. :type perseus_file: string """ # The real cython constructor def __cinit__(self, dimensions=None, top_dimensional_cells=None, - perseus_file=''): - if (dimensions is not None) and (top_dimensional_cells is not None) and (perseus_file == ''): + numpy_array=None, perseus_file=''): + if ((dimensions is not None) and (top_dimensional_cells is not None) + and (numpy_array is None) and (perseus_file == '')): self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) - elif (dimensions is None) and (top_dimensional_cells is None) and (perseus_file != ''): + elif ((dimensions is None) and (top_dimensional_cells is None) + and (numpy_array is not None) and (perseus_file == '')): + if isinstance(numpy_array, np.ndarray): + dimensions = list(numpy_array.shape) + top_dimensional_cells = numpy_array.flatten().tolist() + self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) + else: + print("numpy_array is not an instance of ndarray. It is a " + type(numpy_array)) + elif ((dimensions is None) and (top_dimensional_cells is None) + and (numpy_array is None) and (perseus_file != '')): if os.path.isfile(perseus_file): self.thisptr = new Bitmap_cubical_complex_base_interface(str.encode(perseus_file)) else: @@ -184,4 +199,4 @@ cdef class CubicalComplex: else: print("intervals_in_dim function requires persistence function" " to be launched first.") - return np_array(intervals_result) + return np.array(intervals_result) diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index c89055db..8318b7d3 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -5,7 +5,7 @@ from libcpp.string cimport string from libcpp cimport bool import os -from numpy import array as np_array +import numpy as np # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -47,7 +47,7 @@ cdef class PeriodicCubicalComplex: # Fake constructor that does nothing but documenting the constructor def __init__(self, dimensions=None, top_dimensional_cells=None, - periodic_dimensions=None, perseus_file=''): + numpy_array=None, periodic_dimensions=None, perseus_file=''): """PeriodicCubicalComplex constructor from dimensions and top_dimensional_cells or from a Perseus-style file name. @@ -60,16 +60,41 @@ cdef class PeriodicCubicalComplex: Or + :param numpy_array: Filtration values in a d-array. + :type numpy_array: numpy ndarray + :param periodic_dimensions: A list of top dimensional cells periodicity value. + :type periodic_dimensions: list of boolean + + Or + :param perseus_file: A Perseus-style file name. :type perseus_file: string """ # The real cython constructor def __cinit__(self, dimensions=None, top_dimensional_cells=None, - periodic_dimensions=None, perseus_file=''): - if (dimensions is not None) and (top_dimensional_cells is not None) and (periodic_dimensions is not None) and (perseus_file == ''): - self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, top_dimensional_cells, periodic_dimensions) - elif (dimensions is None) and (top_dimensional_cells is None) and (periodic_dimensions is None) and (perseus_file != ''): + numpy_array=None, periodic_dimensions=None, + perseus_file=''): + if ((dimensions is not None) and (top_dimensional_cells is not None) + and (numpy_array is None) and (periodic_dimensions is not None) + and (perseus_file == '')): + self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, + top_dimensional_cells, + periodic_dimensions) + elif ((dimensions is None) and (top_dimensional_cells is None) + and (numpy_array is not None) and (periodic_dimensions is not None) + and (perseus_file == '')): + if isinstance(numpy_array, np.ndarray): + dimensions = list(numpy_array.shape) + top_dimensional_cells = numpy_array.flatten().tolist() + self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, + top_dimensional_cells, + periodic_dimensions) + else: + print("numpy_array is not an instance of ndarray. It is a " + type(numpy_array)) + elif ((dimensions is None) and (top_dimensional_cells is None) + and (numpy_array is None) and (periodic_dimensions is None) + and (perseus_file != '')): if os.path.isfile(perseus_file): self.thisptr = new Periodic_cubical_complex_base_interface(str.encode(perseus_file)) else: @@ -186,4 +211,4 @@ cdef class PeriodicCubicalComplex: else: print("intervals_in_dim function requires persistence function" " to be launched first.") - return np_array(intervals_result) + return np.array(intervals_result) diff --git a/src/python/test/test_cubical_complex.py b/src/python/test/test_cubical_complex.py index 68f54fbe..a37d7821 100755 --- a/src/python/test/test_cubical_complex.py +++ b/src/python/test/test_cubical_complex.py @@ -1,4 +1,5 @@ from gudhi import CubicalComplex +import numpy as np """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -56,7 +57,7 @@ def test_dimension_or_perseus_file_constructor(): assert cub.__is_persistence_defined() == False -def test_dimension_simple_constructor(): +def simple_constructor(cub): cub = CubicalComplex( dimensions=[3, 3], top_dimensional_cells=[1, 2, 3, 4, 5, 6, 7, 8, 9] ) @@ -67,12 +68,22 @@ def test_dimension_simple_constructor(): assert cub.betti_numbers() == [1, 0, 0] assert cub.persistent_betti_numbers(0, 1000) == [0, 0, 0] - -def test_user_case_simple_constructor(): +def test_simple_constructor_from_top_cells(): cub = CubicalComplex( dimensions=[3, 3], - top_dimensional_cells=[float("inf"), 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + top_dimensional_cells=[1, 2, 3, 4, 5, 6, 7, 8, 9], ) + simple_constructor(cub) + +def test_simple_constructor_from_numpy_array(): + cub = CubicalComplex( + numpy_array=np.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]]) + ) + simple_constructor(cub) + +def user_case_simple_constructor(cub): assert cub.__is_defined() == True assert cub.__is_persistence_defined() == False assert cub.persistence() == [(1, (0.0, 1.0)), (0, (0.0, float("inf")))] @@ -83,6 +94,20 @@ def test_user_case_simple_constructor(): ) assert other_cub.persistence() == [(1, (0.0, 1.0)), (0, (0.0, float("inf")))] +def test_user_case_simple_constructor_from_top_cells(): + cub = CubicalComplex( + dimensions=[3, 3], + top_dimensional_cells=[float("inf"), 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + ) + user_case_simple_constructor(cub) + +def test_user_case_simple_constructor_from_numpy_array(): + cub = CubicalComplex( + numpy_array=np.array([[float("inf"), 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 0.0, 0.0]]) + ) + user_case_simple_constructor(cub) def test_dimension_file_constructor(): # Create test file -- cgit v1.2.3 From b63be266effe24646823acc59fe397021bb13a3e Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 18 Dec 2019 10:44:17 +0100 Subject: Reuse top_dimensional_cells instead of numpy_array argument to create a cubical --- src/python/doc/cubical_complex_user.rst | 2 +- src/python/gudhi/cubical_complex.pyx | 25 ++++++++++--------- src/python/gudhi/periodic_cubical_complex.pyx | 35 +++++++++++++-------------- src/python/test/test_cubical_complex.py | 12 ++++----- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/python/doc/cubical_complex_user.rst b/src/python/doc/cubical_complex_user.rst index 21806038..d56c8789 100644 --- a/src/python/doc/cubical_complex_user.rst +++ b/src/python/doc/cubical_complex_user.rst @@ -143,7 +143,7 @@ Or it can be defined as follows: from gudhi import PeriodicCubicalComplex as pcc from numpy import array as np_array - periodic_cc = pcc(numpy_array = np_array([[0, 0, 0], [0, 1, 0], [0, 0, 0]]), + periodic_cc = pcc(top_dimensional_cells = np_array([[0, 0, 0], [0, 1, 0], [0, 0, 0]]), periodic_dimensions=[True, False]) result_str = 'Periodic cubical complex is of dimension ' + repr(periodic_cc.dimension()) + ' - ' + \ repr(periodic_cc.num_simplices()) + ' simplices.' diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 2ec0146a..1aa1164c 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -47,7 +47,7 @@ cdef class CubicalComplex: # Fake constructor that does nothing but documenting the constructor def __init__(self, dimensions=None, top_dimensional_cells=None, - numpy_array=None, perseus_file=''): + perseus_file=''): """CubicalComplex constructor from dimensions and top_dimensional_cells or from a Perseus-style file name. @@ -58,8 +58,9 @@ cdef class CubicalComplex: Or - :param numpy_array: Filtration values in a d-array. - :type numpy_array: numpy ndarray + :param top_dimensional_cells: A multidimensional array of cells + filtration values. + :type top_dimensional_cells: numpy ndarray Or @@ -69,20 +70,20 @@ cdef class CubicalComplex: # The real cython constructor def __cinit__(self, dimensions=None, top_dimensional_cells=None, - numpy_array=None, perseus_file=''): + perseus_file=''): if ((dimensions is not None) and (top_dimensional_cells is not None) - and (numpy_array is None) and (perseus_file == '')): + and (perseus_file == '')): self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) - elif ((dimensions is None) and (top_dimensional_cells is None) - and (numpy_array is not None) and (perseus_file == '')): - if isinstance(numpy_array, np.ndarray): - dimensions = list(numpy_array.shape) - top_dimensional_cells = numpy_array.flatten().tolist() + elif ((dimensions is None) and (top_dimensional_cells is not None) + and (perseus_file == '')): + if isinstance(top_dimensional_cells, np.ndarray): + dimensions = list(top_dimensional_cells.shape) + top_dimensional_cells = top_dimensional_cells.flatten().tolist() self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) else: - print("numpy_array is not an instance of ndarray. It is a " + type(numpy_array)) + print("top_dimensional_cells is not an instance of ndarray. It is a " + type(top_dimensional_cells)) elif ((dimensions is None) and (top_dimensional_cells is None) - and (numpy_array is None) and (perseus_file != '')): + and (perseus_file != '')): if os.path.isfile(perseus_file): self.thisptr = new Bitmap_cubical_complex_base_interface(str.encode(perseus_file)) else: diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index 8318b7d3..e623058a 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -47,7 +47,7 @@ cdef class PeriodicCubicalComplex: # Fake constructor that does nothing but documenting the constructor def __init__(self, dimensions=None, top_dimensional_cells=None, - numpy_array=None, periodic_dimensions=None, perseus_file=''): + periodic_dimensions=None, perseus_file=''): """PeriodicCubicalComplex constructor from dimensions and top_dimensional_cells or from a Perseus-style file name. @@ -60,8 +60,9 @@ cdef class PeriodicCubicalComplex: Or - :param numpy_array: Filtration values in a d-array. - :type numpy_array: numpy ndarray + :param top_dimensional_cells: A multidimensional array of cells + filtration values. + :type top_dimensional_cells: numpy ndarray :param periodic_dimensions: A list of top dimensional cells periodicity value. :type periodic_dimensions: list of boolean @@ -73,35 +74,33 @@ cdef class PeriodicCubicalComplex: # The real cython constructor def __cinit__(self, dimensions=None, top_dimensional_cells=None, - numpy_array=None, periodic_dimensions=None, - perseus_file=''): + periodic_dimensions=None, perseus_file=''): if ((dimensions is not None) and (top_dimensional_cells is not None) - and (numpy_array is None) and (periodic_dimensions is not None) - and (perseus_file == '')): + and (periodic_dimensions is not None) and (perseus_file == '')): self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, top_dimensional_cells, periodic_dimensions) - elif ((dimensions is None) and (top_dimensional_cells is None) - and (numpy_array is not None) and (periodic_dimensions is not None) - and (perseus_file == '')): - if isinstance(numpy_array, np.ndarray): - dimensions = list(numpy_array.shape) - top_dimensional_cells = numpy_array.flatten().tolist() + elif ((dimensions is None) and (top_dimensional_cells is not None) + and (periodic_dimensions is not None) and (perseus_file == '')): + if isinstance(top_dimensional_cells, np.ndarray): + dimensions = list(top_dimensional_cells.shape) + top_dimensional_cells = top_dimensional_cells.flatten().tolist() self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, top_dimensional_cells, periodic_dimensions) else: - print("numpy_array is not an instance of ndarray. It is a " + type(numpy_array)) + print("top_dimensional_cells is not an instance of ndarray. It is a " + type(top_dimensional_cells)) elif ((dimensions is None) and (top_dimensional_cells is None) - and (numpy_array is None) and (periodic_dimensions is None) - and (perseus_file != '')): + and (periodic_dimensions is None) and (perseus_file != '')): if os.path.isfile(perseus_file): self.thisptr = new Periodic_cubical_complex_base_interface(str.encode(perseus_file)) else: print("file " + perseus_file + " not found.") else: - print("CubicalComplex can be constructed from dimensions and " - "top_dimensional_cells or from a Perseus-style file name.") + print("CubicalComplex can be constructed from dimensions, " + "top_dimensional_cells and periodic_dimensions, or from " + "top_dimensional_cells and periodic_dimensions or from " + "a Perseus-style file name.") def __dealloc__(self): if self.thisptr != NULL: diff --git a/src/python/test/test_cubical_complex.py b/src/python/test/test_cubical_complex.py index a37d7821..6d4e0309 100755 --- a/src/python/test/test_cubical_complex.py +++ b/src/python/test/test_cubical_complex.py @@ -77,9 +77,9 @@ def test_simple_constructor_from_top_cells(): def test_simple_constructor_from_numpy_array(): cub = CubicalComplex( - numpy_array=np.array([[1, 2, 3], - [4, 5, 6], - [7, 8, 9]]) + top_dimensional_cells=np.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]]) ) simple_constructor(cub) @@ -103,9 +103,9 @@ def test_user_case_simple_constructor_from_top_cells(): def test_user_case_simple_constructor_from_numpy_array(): cub = CubicalComplex( - numpy_array=np.array([[float("inf"), 0.0, 0.0], - [0.0, 1.0, 0.0], - [0.0, 0.0, 0.0]]) + top_dimensional_cells=np.array([[float("inf"), 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 0.0, 0.0]]) ) user_case_simple_constructor(cub) -- cgit v1.2.3 From 32a9c5e4df26e59f5f376e54c05c7bec92012cff Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 18 Dec 2019 10:57:31 +0100 Subject: use ravel instead of flatten (that makes a copy) and no more converting to a list --- src/python/gudhi/cubical_complex.pyx | 4 ++-- src/python/gudhi/periodic_cubical_complex.pyx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 1aa1164c..756a9dab 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -77,8 +77,8 @@ cdef class CubicalComplex: elif ((dimensions is None) and (top_dimensional_cells is not None) and (perseus_file == '')): if isinstance(top_dimensional_cells, np.ndarray): - dimensions = list(top_dimensional_cells.shape) - top_dimensional_cells = top_dimensional_cells.flatten().tolist() + dimensions = top_dimensional_cells.shape + top_dimensional_cells = top_dimensional_cells.ravel(order='C') self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) else: print("top_dimensional_cells is not an instance of ndarray. It is a " + type(top_dimensional_cells)) diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index e623058a..c3d64123 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -83,8 +83,8 @@ cdef class PeriodicCubicalComplex: elif ((dimensions is None) and (top_dimensional_cells is not None) and (periodic_dimensions is not None) and (perseus_file == '')): if isinstance(top_dimensional_cells, np.ndarray): - dimensions = list(top_dimensional_cells.shape) - top_dimensional_cells = top_dimensional_cells.flatten().tolist() + dimensions = top_dimensional_cells.shape + top_dimensional_cells = top_dimensional_cells.ravel(order='C') self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, top_dimensional_cells, periodic_dimensions) -- cgit v1.2.3 From ba406558f707f55b638be92d197ae3039595b29a Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 20 Dec 2019 16:57:36 +0100 Subject: Fix dimension cell linearization and test connected sublevel sets --- src/python/gudhi/cubical_complex.pyx | 2 +- src/python/gudhi/periodic_cubical_complex.pyx | 2 +- src/python/test/test_cubical_complex.py | 28 ++++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 756a9dab..04b22921 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -78,7 +78,7 @@ cdef class CubicalComplex: and (perseus_file == '')): if isinstance(top_dimensional_cells, np.ndarray): dimensions = top_dimensional_cells.shape - top_dimensional_cells = top_dimensional_cells.ravel(order='C') + top_dimensional_cells = top_dimensional_cells.ravel(order='F') self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) else: print("top_dimensional_cells is not an instance of ndarray. It is a " + type(top_dimensional_cells)) diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index c3d64123..1d6f1e59 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -84,7 +84,7 @@ cdef class PeriodicCubicalComplex: and (periodic_dimensions is not None) and (perseus_file == '')): if isinstance(top_dimensional_cells, np.ndarray): dimensions = top_dimensional_cells.shape - top_dimensional_cells = top_dimensional_cells.ravel(order='C') + top_dimensional_cells = top_dimensional_cells.ravel(order='F') self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, top_dimensional_cells, periodic_dimensions) diff --git a/src/python/test/test_cubical_complex.py b/src/python/test/test_cubical_complex.py index 6d4e0309..121da12a 100755 --- a/src/python/test/test_cubical_complex.py +++ b/src/python/test/test_cubical_complex.py @@ -1,4 +1,4 @@ -from gudhi import CubicalComplex +from gudhi import CubicalComplex, PeriodicCubicalComplex import numpy as np """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. @@ -121,3 +121,29 @@ def test_dimension_file_constructor(): assert cub.__is_persistence_defined() == True assert cub.betti_numbers() == [1, 0, 0] assert cub.persistent_betti_numbers(0, 1000) == [1, 0, 0] + +def test_connected_sublevel_sets(): + array_cells = np.array([[3, 3], [2, 2], [4, 4]]) + linear_cells = [3, 3, 2, 2, 4, 4] + dimensions = [2, 3] + periodic_dimensions = [False, False] + # with a numpy array version + cub = CubicalComplex(top_dimensional_cells = array_cells) + assert cub.persistence() == [(0, (2.0, float("inf")))] + assert cub.betti_numbers() == [1, 0, 0] + # with vector of dimensions + cub = CubicalComplex(dimensions = dimensions, + top_dimensional_cells = linear_cells) + assert cub.persistence() == [(0, (2.0, float("inf")))] + assert cub.betti_numbers() == [1, 0, 0] + # periodic with a numpy array version + cub = PeriodicCubicalComplex(top_dimensional_cells = array_cells, + periodic_dimensions = periodic_dimensions) + assert cub.persistence() == [(0, (2.0, float("inf")))] + assert cub.betti_numbers() == [1, 0, 0] + # periodic with vector of dimensions + cub = PeriodicCubicalComplex(dimensions = dimensions, + top_dimensional_cells = linear_cells, + periodic_dimensions = periodic_dimensions) + assert cub.persistence() == [(0, (2.0, float("inf")))] + assert cub.betti_numbers() == [1, 0, 0] -- cgit v1.2.3 From ba35ad0281f89449655a0144284f50e22e870ab4 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 26 Dec 2019 19:32:34 +0100 Subject: Install pybind11 in CircleCI --- Dockerfile_for_circleci_image | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile_for_circleci_image b/Dockerfile_for_circleci_image index f0c73d76..c9342bf4 100644 --- a/Dockerfile_for_circleci_image +++ b/Dockerfile_for_circleci_image @@ -43,6 +43,7 @@ RUN apt-get install -y make \ python3-pip \ python3-pytest \ python3-tk \ + python3-pybind11 \ libfreetype6-dev \ pkg-config -- cgit v1.2.3 From 7e9777c568d32a2c3d5c6fd85bbbf882bb121b83 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Thu, 26 Dec 2019 20:50:07 +0100 Subject: I am also going to need git to checkout submodules, apparently --- Dockerfile_for_circleci_image | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile_for_circleci_image b/Dockerfile_for_circleci_image index c9342bf4..ff4e6018 100644 --- a/Dockerfile_for_circleci_image +++ b/Dockerfile_for_circleci_image @@ -25,6 +25,7 @@ ENV LC_ALL en_US.UTF-8 # Required for Gudhi compilation RUN apt-get install -y make \ + git \ g++ \ cmake \ graphviz \ -- cgit v1.2.3 From d316f8ca62a3335d530784a71b4110bc2219c2dd Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Fri, 3 Jan 2020 22:02:48 +0100 Subject: Remove implicit this capture in [=] --- src/Nerve_GIC/include/gudhi/GIC.h | 2 +- src/Simplex_tree/include/gudhi/Simplex_tree.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nerve_GIC/include/gudhi/GIC.h b/src/Nerve_GIC/include/gudhi/GIC.h index b8169c59..a47d6889 100644 --- a/src/Nerve_GIC/include/gudhi/GIC.h +++ b/src/Nerve_GIC/include/gudhi/GIC.h @@ -707,7 +707,7 @@ class Cover_complex { // Sort points according to function values std::vector points(n); for (int i = 0; i < n; i++) points[i] = i; - std::sort(points.begin(), points.end(), [=](const int & p1, const int & p2){return (this->func[p1] < this->func[p2]);}); + std::sort(points.begin(), points.end(), [this](int p1, int p2){return (this->func[p1] < this->func[p2]);}); int id = 0; int pos = 0; diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index fafdb01c..76608008 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -1378,7 +1378,7 @@ class Simplex_tree { private: bool rec_prune_above_filtration(Siblings* sib, Filtration_value filt) { auto&& list = sib->members(); - auto last = std::remove_if(list.begin(), list.end(), [=](Dit_value_t& simplex) { + auto last = std::remove_if(list.begin(), list.end(), [this,filt](Dit_value_t& simplex) { if (simplex.second.filtration() <= filt) return false; if (has_children(&simplex)) rec_delete(simplex.second.children()); // dimension may need to be lowered -- cgit v1.2.3 From d832d3585bf47c48f28d861469730fc47f4956c8 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 7 Jan 2020 12:24:45 +0100 Subject: Code review: less strict with numpy arrays for cubical ctors. Also accept lists --- src/python/gudhi/cubical_complex.pyx | 14 +++++++------- src/python/gudhi/periodic_cubical_complex.pyx | 18 +++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 04b22921..63911478 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -60,7 +60,7 @@ cdef class CubicalComplex: :param top_dimensional_cells: A multidimensional array of cells filtration values. - :type top_dimensional_cells: numpy ndarray + :type top_dimensional_cells: numpy ndarray or list of list of double Or @@ -76,12 +76,12 @@ cdef class CubicalComplex: self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) elif ((dimensions is None) and (top_dimensional_cells is not None) and (perseus_file == '')): - if isinstance(top_dimensional_cells, np.ndarray): - dimensions = top_dimensional_cells.shape - top_dimensional_cells = top_dimensional_cells.ravel(order='F') - self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) - else: - print("top_dimensional_cells is not an instance of ndarray. It is a " + type(top_dimensional_cells)) + top_dimensional_cells = np.array(top_dimensional_cells, + copy = False, + order = 'F') + dimensions = top_dimensional_cells.shape + top_dimensional_cells = top_dimensional_cells.ravel(order='F') + self.thisptr = new Bitmap_cubical_complex_base_interface(dimensions, top_dimensional_cells) elif ((dimensions is None) and (top_dimensional_cells is None) and (perseus_file != '')): if os.path.isfile(perseus_file): diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index 1d6f1e59..3c0f529b 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -62,7 +62,7 @@ cdef class PeriodicCubicalComplex: :param top_dimensional_cells: A multidimensional array of cells filtration values. - :type top_dimensional_cells: numpy ndarray + :type top_dimensional_cells: numpy ndarray or list of list of double :param periodic_dimensions: A list of top dimensional cells periodicity value. :type periodic_dimensions: list of boolean @@ -82,14 +82,14 @@ cdef class PeriodicCubicalComplex: periodic_dimensions) elif ((dimensions is None) and (top_dimensional_cells is not None) and (periodic_dimensions is not None) and (perseus_file == '')): - if isinstance(top_dimensional_cells, np.ndarray): - dimensions = top_dimensional_cells.shape - top_dimensional_cells = top_dimensional_cells.ravel(order='F') - self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, - top_dimensional_cells, - periodic_dimensions) - else: - print("top_dimensional_cells is not an instance of ndarray. It is a " + type(top_dimensional_cells)) + top_dimensional_cells = np.array(top_dimensional_cells, + copy = False, + order = 'F') + dimensions = top_dimensional_cells.shape + top_dimensional_cells = top_dimensional_cells.ravel(order='F') + self.thisptr = new Periodic_cubical_complex_base_interface(dimensions, + top_dimensional_cells, + periodic_dimensions) elif ((dimensions is None) and (top_dimensional_cells is None) and (periodic_dimensions is None) and (perseus_file != '')): if os.path.isfile(perseus_file): -- cgit v1.2.3 From 82b556c554a60e3b05942ba53300463dc00b8538 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 7 Jan 2020 12:27:19 +0100 Subject: Code review: use list in examples instead of numpy arrays --- src/python/doc/cubical_complex_user.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/python/doc/cubical_complex_user.rst b/src/python/doc/cubical_complex_user.rst index d56c8789..6fdcd9df 100644 --- a/src/python/doc/cubical_complex_user.rst +++ b/src/python/doc/cubical_complex_user.rst @@ -142,9 +142,7 @@ Or it can be defined as follows: .. testcode:: from gudhi import PeriodicCubicalComplex as pcc - from numpy import array as np_array - periodic_cc = pcc(top_dimensional_cells = np_array([[0, 0, 0], [0, 1, 0], [0, 0, 0]]), - periodic_dimensions=[True, False]) + periodic_cc = pcc(top_dimensional_cells = [[0, 0, 0], [0, 1, 0], [0, 0, 0]] result_str = 'Periodic cubical complex is of dimension ' + repr(periodic_cc.dimension()) + ' - ' + \ repr(periodic_cc.num_simplices()) + ' simplices.' print(result_str) -- cgit v1.2.3 From 44484a8f9242e45ead34cb49226df015c4b6c19a Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 7 Jan 2020 12:27:43 +0100 Subject: Code review: use list in examples instead of numpy arrays --- src/python/doc/cubical_complex_user.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/python/doc/cubical_complex_user.rst b/src/python/doc/cubical_complex_user.rst index 6fdcd9df..56cf0170 100644 --- a/src/python/doc/cubical_complex_user.rst +++ b/src/python/doc/cubical_complex_user.rst @@ -142,7 +142,8 @@ Or it can be defined as follows: .. testcode:: from gudhi import PeriodicCubicalComplex as pcc - periodic_cc = pcc(top_dimensional_cells = [[0, 0, 0], [0, 1, 0], [0, 0, 0]] + periodic_cc = pcc(top_dimensional_cells = [[0, 0, 0], [0, 1, 0], [0, 0, 0]], + periodic_dimensions=[True, False]) result_str = 'Periodic cubical complex is of dimension ' + repr(periodic_cc.dimension()) + ' - ' + \ repr(periodic_cc.num_simplices()) + ' simplices.' print(result_str) -- cgit v1.2.3 From 6a7a7b6d6d81946dd903f68f525389ab2fd51f71 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 7 Jan 2020 13:31:18 +0100 Subject: Doc review: anything convertible to a numpy ndarray --- src/python/gudhi/cubical_complex.pyx | 2 +- src/python/gudhi/periodic_cubical_complex.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 63911478..b7047d4f 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -60,7 +60,7 @@ cdef class CubicalComplex: :param top_dimensional_cells: A multidimensional array of cells filtration values. - :type top_dimensional_cells: numpy ndarray or list of list of double + :type top_dimensional_cells: anything convertible to a numpy ndarray Or diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index 3c0f529b..eb96ab5f 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -62,7 +62,7 @@ cdef class PeriodicCubicalComplex: :param top_dimensional_cells: A multidimensional array of cells filtration values. - :type top_dimensional_cells: numpy ndarray or list of list of double + :type top_dimensional_cells: anything convertible to a numpy ndarray :param periodic_dimensions: A list of top dimensional cells periodicity value. :type periodic_dimensions: list of boolean -- cgit v1.2.3 From 98b4aa0026a7208230d396bc9fb28c7a3b72be6d Mon Sep 17 00:00:00 2001 From: tlacombe Date: Tue, 7 Jan 2020 18:06:13 +0100 Subject: changing variable name from (p, q) to (q, internal_p). Must also be done in tests files and doc --- src/python/gudhi/wasserstein.py | 46 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index d8a3104c..2acf93d6 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -23,26 +23,26 @@ def _proj_on_diag(X): return np.array([Z , Z]).T -def _build_dist_matrix(X, Y, p=2., q=2.): +def _build_dist_matrix(X, Y, q=2., internal_p=2.): ''' :param X: (n x 2) numpy.array encoding the (points of the) first diagram. :param Y: (m x 2) numpy.array encoding the second diagram. - :param q: Ground metric (i.e. norm l_q). - :param p: exponent for the Wasserstein metric. + :param internal_p: Ground metric (i.e. norm l_q). + :param q: exponent for the Wasserstein metric. :returns: (n+1) x (m+1) np.array encoding the cost matrix C. For 1 <= i <= n, 1 <= j <= m, C[i,j] encodes the distance between X[i] and Y[j], while C[i, m+1] (resp. C[n+1, j]) encodes the distance (to the p) between X[i] (resp Y[j]) and its orthogonal proj onto the diagonal. note also that C[n+1, m+1] = 0 (it costs nothing to move from the diagonal to the diagonal). ''' Xdiag = _proj_on_diag(X) Ydiag = _proj_on_diag(Y) - if np.isinf(q): - C = sc.cdist(X,Y, metric='chebyshev')**p - Cxd = np.linalg.norm(X - Xdiag, ord=q, axis=1)**p - Cdy = np.linalg.norm(Y - Ydiag, ord=q, axis=1)**p + if np.isinf(internal_p): + C = sc.cdist(X,Y, metric='chebyshev')**q + Cxd = np.linalg.norm(X - Xdiag, ord=internal_p, axis=1)**q + Cdy = np.linalg.norm(Y - Ydiag, ord=internal_p, axis=1)**q else: - C = sc.cdist(X,Y, metric='minkowski', p=q)**p - Cxd = np.linalg.norm(X - Xdiag, ord=q, axis=1)**p - Cdy = np.linalg.norm(Y - Ydiag, ord=q, axis=1)**p + C = sc.cdist(X,Y, metric='minkowski', p=internal_p)**q + Cxd = np.linalg.norm(X - Xdiag, ord=internal_p, axis=1)**q + Cdy = np.linalg.norm(Y - Ydiag, ord=internal_p, axis=1)**q Cf = np.hstack((C, Cxd[:,None])) Cdy = np.append(Cdy, 0) @@ -51,24 +51,24 @@ def _build_dist_matrix(X, Y, p=2., q=2.): return Cf -def _perstot(X, p, q): +def _perstot(X, q, internal_p): ''' :param X: (n x 2) numpy.array (points of a given diagram). - :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (Euclidean norm). - :param p: exponent for Wasserstein; Default value is 2. + :param internal_p: Ground metric on the (upper-half) plane (i.e. norm l_p in R^2); Default value is 2 (Euclidean norm). + :param q: exponent for Wasserstein; Default value is 2. :returns: float, the total persistence of the diagram (that is, its distance to the empty diagram). ''' Xdiag = _proj_on_diag(X) - return (np.sum(np.linalg.norm(X - Xdiag, ord=q, axis=1)**p))**(1./p) + return (np.sum(np.linalg.norm(X - Xdiag, ord=internal_p, axis=1)**q))**(1./q) -def wasserstein_distance(X, Y, p=2., q=2.): +def wasserstein_distance(X, Y, q=2., internal_p=2.): ''' :param X: (n x 2) numpy.array encoding the (finite points of the) first diagram. Must not contain essential points (i.e. with infinite coordinate). :param Y: (m x 2) numpy.array encoding the second diagram. - :param q: Ground metric on the (upper-half) plane (i.e. norm l_q in R^2); Default value is 2 (euclidean norm). - :param p: exponent for Wasserstein; Default value is 2. - :returns: the p-Wasserstein distance (1 <= p < infinity) with respect to the q-norm as ground metric. + :param internal_p: Ground metric on the (upper-half) plane (i.e. norm l_p in R^2); Default value is 2 (euclidean norm). + :param q: exponent for Wasserstein; Default value is 2. + :returns: the q-Wasserstein distance (1 <= q < infinity) with respect to the internal_p-norm as ground metric. :rtype: float ''' n = len(X) @@ -79,20 +79,20 @@ def wasserstein_distance(X, Y, p=2., q=2.): if Y.size == 0: return 0. else: - return _perstot(Y, p, q) + return _perstot(Y, q, internal_p) elif Y.size == 0: - return _perstot(X, p, q) + return _perstot(X, q, internal_p) - M = _build_dist_matrix(X, Y, p=p, q=q) + M = _build_dist_matrix(X, Y, q=q, internal_p=internal_p) a = np.full(n+1, 1. / (n + m) ) # weight vector of the input diagram. Uniform here. a[-1] = a[-1] * m # normalized so that we have a probability measure, required by POT b = np.full(m+1, 1. / (n + m) ) # weight vector of the input diagram. Uniform here. b[-1] = b[-1] * n # so that we have a probability measure, required by POT # Comptuation of the otcost using the ot.emd2 library. - # Note: it is the squared Wasserstein distance. + # Note: it is the Wasserstein distance to the power q. # The default numItermax=100000 is not sufficient for some examples with 5000 points, what is a good value? ot_cost = (n+m) * ot.emd2(a, b, M, numItermax=2000000) - return ot_cost ** (1./p) + return ot_cost ** (1./q) -- cgit v1.2.3 From f3dc8e802d2a6226532f92a252f96ddbd7b6a411 Mon Sep 17 00:00:00 2001 From: tlacombe Date: Wed, 8 Jan 2020 09:41:31 +0100 Subject: changing variable name in test files --- src/python/test/test_wasserstein_distance.py | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/python/test/test_wasserstein_distance.py b/src/python/test/test_wasserstein_distance.py index a6bf9901..40112c1b 100755 --- a/src/python/test/test_wasserstein_distance.py +++ b/src/python/test/test_wasserstein_distance.py @@ -23,26 +23,26 @@ def test_basic_wasserstein(): diag4 = np.array([[0, 3], [4, 8]]) emptydiag = np.array([[]]) - assert wasserstein_distance(emptydiag, emptydiag, q=2., p=1.) == 0. - assert wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=1.) == 0. - assert wasserstein_distance(emptydiag, emptydiag, q=np.inf, p=2.) == 0. - assert wasserstein_distance(emptydiag, emptydiag, q=2., p=2.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, internal_p=2., q=1.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, internal_p=np.inf, q=1.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, internal_p=np.inf, q=2.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, internal_p=2., q=2.) == 0. - assert wasserstein_distance(diag3, emptydiag, q=np.inf, p=1.) == 2. - assert wasserstein_distance(diag3, emptydiag, q=1., p=1.) == 4. + assert wasserstein_distance(diag3, emptydiag, internal_p=np.inf, q=1.) == 2. + assert wasserstein_distance(diag3, emptydiag, internal_p=1., q=1.) == 4. - assert wasserstein_distance(diag4, emptydiag, q=1., p=2.) == 5. # thank you Pythagorician triplets - assert wasserstein_distance(diag4, emptydiag, q=np.inf, p=2.) == 2.5 - assert wasserstein_distance(diag4, emptydiag, q=2., p=2.) == 3.5355339059327378 + assert wasserstein_distance(diag4, emptydiag, internal_p=1., q=2.) == 5. # thank you Pythagorician triplets + assert wasserstein_distance(diag4, emptydiag, internal_p=np.inf, q=2.) == 2.5 + assert wasserstein_distance(diag4, emptydiag, internal_p=2., q=2.) == 3.5355339059327378 - assert wasserstein_distance(diag1, diag2, q=2., p=1.) == 1.4453593023967701 - assert wasserstein_distance(diag1, diag2, q=2.35, p=1.74) == 0.9772734057168739 + assert wasserstein_distance(diag1, diag2, internal_p=2., q=1.) == 1.4453593023967701 + assert wasserstein_distance(diag1, diag2, internal_p=2.35, q=1.74) == 0.9772734057168739 - assert wasserstein_distance(diag1, emptydiag, q=2.35, p=1.7863) == 3.141592214572228 + assert wasserstein_distance(diag1, emptydiag, internal_p=2.35, q=1.7863) == 3.141592214572228 - assert wasserstein_distance(diag3, diag4, q=1., p=1.) == 3. - assert wasserstein_distance(diag3, diag4, q=np.inf, p=1.) == 3. # no diag matching here - assert wasserstein_distance(diag3, diag4, q=np.inf, p=2.) == np.sqrt(5) - assert wasserstein_distance(diag3, diag4, q=1., p=2.) == np.sqrt(5) - assert wasserstein_distance(diag3, diag4, q=4.5, p=2.) == np.sqrt(5) + assert wasserstein_distance(diag3, diag4, internal_p=1., q=1.) == 3. + assert wasserstein_distance(diag3, diag4, internal_p=np.inf, q=1.) == 3. # no diag matching here + assert wasserstein_distance(diag3, diag4, internal_p=np.inf, q=2.) == np.sqrt(5) + assert wasserstein_distance(diag3, diag4, internal_p=1., q=2.) == np.sqrt(5) + assert wasserstein_distance(diag3, diag4, internal_p=4.5, q=2.) == np.sqrt(5) -- cgit v1.2.3 From 4bcdd64974900302f420fb08435275cc8faa794a Mon Sep 17 00:00:00 2001 From: tlacombe Date: Wed, 8 Jan 2020 09:44:36 +0100 Subject: update variable name in doc --- src/python/doc/wasserstein_distance_sum.inc | 6 +++--- src/python/doc/wasserstein_distance_user.rst | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python/doc/wasserstein_distance_sum.inc b/src/python/doc/wasserstein_distance_sum.inc index ffd4d312..a97f428d 100644 --- a/src/python/doc/wasserstein_distance_sum.inc +++ b/src/python/doc/wasserstein_distance_sum.inc @@ -2,12 +2,12 @@ :widths: 30 50 20 +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ - | .. figure:: | The p-Wasserstein distance measures the similarity between two | :Author: Theo Lacombe | + | .. figure:: | The q-Wasserstein distance measures the similarity between two | :Author: Theo Lacombe | | ../../doc/Bottleneck_distance/perturb_pd.png | persistence diagrams. It's the minimum value c that can be achieved | | | :figclass: align-center | by a perfect matching between the points of the two diagrams (+ all | :Introduced in: GUDHI 3.1.0 | | | diagonal points), where the value of a matching is defined as the | | - | Wasserstein distance is the p-th root of the sum of the | p-th root of the sum of all edge lengths to the power p. Edge lengths| :Copyright: MIT | - | edge lengths to the power p. | are measured in norm q, for :math:`1 \leq q \leq \infty`. | | + | Wasserstein distance is the q-th root of the sum of the | q-th root of the sum of all edge lengths to the power q. Edge lengths| :Copyright: MIT | + | edge lengths to the power q. | are measured in norm p, for :math:`1 \leq p \leq \infty`. | | | | | :Requires: Python Optimal Transport (POT) :math:`\geq` 0.5.1 | +-----------------------------------------------------------------+----------------------------------------------------------------------+------------------------------------------------------------------+ | * :doc:`wasserstein_distance_user` | | diff --git a/src/python/doc/wasserstein_distance_user.rst b/src/python/doc/wasserstein_distance_user.rst index a049cfb5..8862a5ce 100644 --- a/src/python/doc/wasserstein_distance_user.rst +++ b/src/python/doc/wasserstein_distance_user.rst @@ -30,7 +30,7 @@ Note that persistence diagrams must be submitted as (n x 2) numpy arrays and mus diag1 = np.array([[2.7, 3.7],[9.6, 14.],[34.2, 34.974]]) diag2 = np.array([[2.8, 4.45],[9.5, 14.1]]) - message = "Wasserstein distance value = " + '%.2f' % gudhi.wasserstein.wasserstein_distance(diag1, diag2, q=2., p=1.) + message = "Wasserstein distance value = " + '%.2f' % gudhi.wasserstein.wasserstein_distance(diag1, diag2, internal_p=2., q=1.) print(message) The output is: -- cgit v1.2.3 From 6d30726d9279a2ccaacdae6244fd50a6fd34528c Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 10 Jan 2020 10:59:32 +0100 Subject: Fix #105: Add alpha value on the picture, clarify simplices removal from the Delaunay complex, use max_alpha_square=32 in the Python example --- src/Alpha_complex/doc/Intro_alpha_complex.h | 14 +++++++------- .../doc/alpha_complex_representation.ipe | 6 +++--- .../doc/alpha_complex_representation.png | Bin 14606 -> 19568 bytes src/common/doc/main_page.md | 4 ++-- src/python/doc/alpha_complex_sum.inc | 6 +++--- src/python/doc/alpha_complex_user.rst | 19 +++++++++---------- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/Alpha_complex/doc/Intro_alpha_complex.h b/src/Alpha_complex/doc/Intro_alpha_complex.h index 3c32a1e6..a8b1a106 100644 --- a/src/Alpha_complex/doc/Intro_alpha_complex.h +++ b/src/Alpha_complex/doc/Intro_alpha_complex.h @@ -31,8 +31,8 @@ namespace alpha_complex { * circumsphere is empty (the simplex is then said to be Gabriel), and as the minimum of the filtration * values of the codimension 1 cofaces that make it not Gabriel otherwise. * - * All simplices that have a filtration value strictly greater than a given alpha squared value are not inserted into - * the complex. + * All simplices that have a filtration value \f$ > \alpha^2 \f$ are removed from the Delaunay complex + * when creating the simplicial complex if it is specified. * * \image html "alpha_complex_representation.png" "Alpha-complex representation" * @@ -46,8 +46,8 @@ namespace alpha_complex { * \cite cgal:s-gkd-19b from CGAL as template parameter. * * \remark - * - When the simplicial complex is constructed with an infinite value of alpha, the complex is a Delaunay - * complex. + * - When an \f$\alpha\f$-complex is constructed with an infinite value of \f$ \alpha^2 \f$, the complex is a Delaunay + * complex (with special filtration values). * - For people only interested in the topology of the \ref alpha_complex (for instance persistence), * \ref alpha_complex is equivalent to the \ref cech_complex and much smaller if you do not bound the radii. * \ref cech_complex can still make sense in higher dimension precisely because you can bound the radii. @@ -135,13 +135,13 @@ namespace alpha_complex { * * \subsubsection nondecreasing Non decreasing filtration values * - * As the squared radii computed by CGAL are an approximation, it might happen that these alpha squared values do not - * quite define a proper filtration (i.e. non-decreasing with respect to inclusion). + * As the squared radii computed by CGAL are an approximation, it might happen that these \f$ \alpha^2 \f$ values do + * not quite define a proper filtration (i.e. non-decreasing with respect to inclusion). * We fix that up by calling `SimplicialComplexForAlpha::make_filtration_non_decreasing()`. * * \subsubsection pruneabove Prune above given filtration value * - * The simplex tree is pruned from the given maximum alpha squared value (cf. + * The simplex tree is pruned from the given maximum \f$ \alpha^2 \f$ value (cf. * `SimplicialComplexForAlpha::prune_above_filtration()`). * In the following example, the value is given by the user as argument of the program. * diff --git a/src/Alpha_complex/doc/alpha_complex_representation.ipe b/src/Alpha_complex/doc/alpha_complex_representation.ipe index e8096b93..40ff1d0f 100644 --- a/src/Alpha_complex/doc/alpha_complex_representation.ipe +++ b/src/Alpha_complex/doc/alpha_complex_representation.ipe @@ -1,7 +1,7 @@ - - + + @@ -305,7 +305,7 @@ h 108.275 743.531 m 166.45 743.531 l -$\alpha$ +\alpha = \sqrt{32.0} diff --git a/src/Alpha_complex/doc/alpha_complex_representation.png b/src/Alpha_complex/doc/alpha_complex_representation.png index 7b81cd69..5ebb1e75 100644 Binary files a/src/Alpha_complex/doc/alpha_complex_representation.png and b/src/Alpha_complex/doc/alpha_complex_representation.png differ diff --git a/src/common/doc/main_page.md b/src/common/doc/main_page.md index e8d11fdf..0b4bfb7a 100644 --- a/src/common/doc/main_page.md +++ b/src/common/doc/main_page.md @@ -43,8 +43,8 @@ The filtration value of each simplex is computed as the square of the circumradius of the simplex if the circumsphere is empty (the simplex is then said to be Gabriel), and as the minimum of the filtration values of the codimension 1 cofaces that make it not Gabriel otherwise. - All simplices that have a filtration value strictly greater than a given alpha squared value are not inserted into - the complex.
+ All simplices that have a filtration value \f$ > \alpha^2 \f$ are removed from the Delaunay complex + when creating the simplicial complex if it is specified.
For performances reasons, it is advised to use \ref cgal ≥ 5.0.0. diff --git a/src/python/doc/alpha_complex_sum.inc b/src/python/doc/alpha_complex_sum.inc index c5ba9dc7..a1184663 100644 --- a/src/python/doc/alpha_complex_sum.inc +++ b/src/python/doc/alpha_complex_sum.inc @@ -9,9 +9,9 @@ | | circumradius of the simplex if the circumsphere is empty (the simplex | :Copyright: MIT (`GPL v3 `_) | | | is then said to be Gabriel), and as the minimum of the filtration | | | | values of the codimension 1 cofaces that make it not Gabriel | :Requires: `Eigen `__ :math:`\geq` 3.1.0 and `CGAL `__ :math:`\geq` 4.11.0 | - | | otherwise. All simplices that have a filtration value strictly | | - | | greater than a given alpha squared value are not inserted into the | | - | | complex. | | + | | otherwise. All simplices that have a filtration value | | + | | :math:`> \alpha^2` are removed from the Delaunay complex | | + | | when creating the simplicial complex if it is specified. | | | | | | | | This package requires having CGAL version 4.7 or higher (4.8.1 is | | | | advised for better performance). | | diff --git a/src/python/doc/alpha_complex_user.rst b/src/python/doc/alpha_complex_user.rst index b7e69e12..60319e84 100644 --- a/src/python/doc/alpha_complex_user.rst +++ b/src/python/doc/alpha_complex_user.rst @@ -16,7 +16,8 @@ Definition Remarks ^^^^^^^ -When an :math:`\alpha`-complex is constructed with an infinite value of :math:`\alpha`, the complex is a Delaunay complex (with special filtration values). +When an :math:`\alpha`-complex is constructed with an infinite value of :math:`\alpha^2`, +the complex is a Delaunay complex (with special filtration values). Example from points ------------------- @@ -137,19 +138,20 @@ sets the filtration value (0 in case of a vertex - propagation will have no effe Non decreasing filtration values ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -As the squared radii computed by CGAL are an approximation, it might happen that these alpha squared values do not -quite define a proper filtration (i.e. non-decreasing with respect to inclusion). +As the squared radii computed by CGAL are an approximation, it might happen that these +:math:`\alpha^2` values do not quite define a proper filtration (i.e. non-decreasing with +respect to inclusion). We fix that up by calling :func:`~gudhi.SimplexTree.make_filtration_non_decreasing` (cf. `C++ version `_). Prune above given filtration value ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The simplex tree is pruned from the given maximum alpha squared value (cf. +The simplex tree is pruned from the given maximum :math:`\alpha^2` value (cf. :func:`~gudhi.SimplexTree.prune_above_filtration`). Note that this does not provide any kind of speed-up, since we always first build the full filtered complex, so it is recommended not to use :paramref:`~gudhi.AlphaComplex.create_simplex_tree.max_alpha_square`. -In the following example, a threshold of 59 is used. +In the following example, a threshold of :math:`\alpha^2 = 32.0` is used. Example from OFF file @@ -166,7 +168,7 @@ Then, it is asked to display information about the alpha complex: import gudhi alpha_complex = gudhi.AlphaComplex(off_file=gudhi.__root_source_dir__ + \ '/data/points/alphacomplexdoc.off') - simplex_tree = alpha_complex.create_simplex_tree(max_alpha_square=59.0) + simplex_tree = alpha_complex.create_simplex_tree(max_alpha_square=32.0) result_str = 'Alpha complex is of dimension ' + repr(simplex_tree.dimension()) + ' - ' + \ repr(simplex_tree.num_simplices()) + ' simplices - ' + \ repr(simplex_tree.num_vertices()) + ' vertices.' @@ -179,7 +181,7 @@ the program output is: .. testoutput:: - Alpha complex is of dimension 2 - 23 simplices - 7 vertices. + Alpha complex is of dimension 2 - 20 simplices - 7 vertices. [0] -> 0.00 [1] -> 0.00 [2] -> 0.00 @@ -200,9 +202,6 @@ the program output is: [4, 6] -> 22.74 [4, 5, 6] -> 22.74 [3, 6] -> 30.25 - [2, 6] -> 36.50 - [2, 3, 6] -> 36.50 - [2, 4, 6] -> 37.24 CGAL citations ============== -- cgit v1.2.3 From 609d4a5307cb8faef5f5002be9f51eee7bf3916e Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 10 Jan 2020 11:20:07 +0100 Subject: Bug fix: a href tag was badly closed --- src/common/doc/installation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index d70a2efa..ce2c5448 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -33,7 +33,7 @@ make \endverbatim * \subsection testsuites Test suites * To test your build, run the following command in a terminal: * \verbatim make test \endverbatim - * `make test` is using
Ctest<\a> (CMake test driver + * `make test` is using Ctest (CMake test driver * program). If some of the tests are failing, please send us the result of the following command: * \verbatim ctest --output-on-failure \endverbatim * -- cgit v1.2.3 From 8f5cb043bf78b8f008b67e292668543a899e6795 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 10 Jan 2020 19:43:04 +0100 Subject: Reorder import and docstrings. Modify test_representations --- src/python/gudhi/__init__.py.in | 8 +++----- src/python/gudhi/alpha_complex.pyx | 18 +++++++++--------- src/python/gudhi/bottleneck.pyx | 10 +++++----- src/python/gudhi/cubical_complex.pyx | 18 +++++++++--------- src/python/gudhi/euclidean_strong_witness_complex.pyx | 16 ++++++++-------- src/python/gudhi/euclidean_witness_complex.pyx | 16 ++++++++-------- src/python/gudhi/nerve_gic.pyx | 18 +++++++++--------- src/python/gudhi/off_reader.pyx | 10 +++++----- src/python/gudhi/periodic_cubical_complex.pyx | 18 +++++++++--------- src/python/gudhi/persistence_graphical_tools.py | 14 +++++++------- src/python/gudhi/reader_utils.pyx | 18 +++++++++--------- src/python/gudhi/rips_complex.pyx | 18 +++++++++--------- src/python/gudhi/simplex_tree.pxd | 19 +++++++++---------- src/python/gudhi/simplex_tree.pyx | 8 ++++---- src/python/gudhi/strong_witness_complex.pyx | 16 ++++++++-------- src/python/gudhi/subsampling.pyx | 12 ++++++------ src/python/gudhi/tangential_complex.pyx | 18 +++++++++--------- src/python/gudhi/wasserstein.py | 14 +++++++------- src/python/gudhi/witness_complex.pyx | 16 ++++++++-------- src/python/setup.py.in | 8 ++++---- src/python/test/test_alpha_complex.py | 12 ++++++------ src/python/test/test_bottleneck_distance.py | 4 ++-- src/python/test/test_cover_complex.py | 4 ++-- src/python/test/test_cubical_complex.py | 6 +++--- src/python/test/test_euclidean_witness_complex.py | 4 ++-- src/python/test/test_reader_utils.py | 6 +++--- src/python/test/test_representations.py | 15 ++++++++------- src/python/test/test_rips_complex.py | 6 +++--- src/python/test/test_simplex_tree.py | 4 ++-- src/python/test/test_subsampling.py | 4 ++-- src/python/test/test_tangential_complex.py | 4 ++-- src/python/test/test_wasserstein_distance.py | 6 +++--- src/python/test/test_witness_complex.py | 4 ++-- 33 files changed, 185 insertions(+), 187 deletions(-) diff --git a/src/python/gudhi/__init__.py.in b/src/python/gudhi/__init__.py.in index 0c462b02..79e12fbc 100644 --- a/src/python/gudhi/__init__.py.in +++ b/src/python/gudhi/__init__.py.in @@ -1,5 +1,3 @@ -from importlib import import_module - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -9,6 +7,9 @@ from importlib import import_module # Modification(s): # - YYYY/MM Author: Description of the modification +from importlib import import_module +from sys import exc_info + __author__ = "GUDHI Editorial Board" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "https://gudhi.inria.fr/licensing/" @@ -17,9 +18,6 @@ __version__ = "@GUDHI_VERSION@" __root_source_dir__ = "@CMAKE_SOURCE_DIR@" __debug_info__ = @GUDHI_PYTHON_DEBUG_INFO@ -from sys import exc_info -from importlib import import_module - __all__ = [@GUDHI_PYTHON_MODULES@ @GUDHI_PYTHON_MODULES_EXTRA@] __available_modules = '' diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index 24e36bea..db11416c 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -1,3 +1,12 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + from cython cimport numeric from libcpp.vector cimport vector from libcpp.utility cimport pair @@ -9,15 +18,6 @@ import os from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Vincent Rouvreau -# -# Copyright (C) 2016 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "GPL v3" diff --git a/src/python/gudhi/bottleneck.pyx b/src/python/gudhi/bottleneck.pyx index c2361024..af011e88 100644 --- a/src/python/gudhi/bottleneck.pyx +++ b/src/python/gudhi/bottleneck.pyx @@ -1,8 +1,3 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.utility cimport pair -import os - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -12,6 +7,11 @@ import os # Modification(s): # - YYYY/MM Author: Description of the modification +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.utility cimport pair +import os + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "GPL v3" diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index b7047d4f..92ff6411 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -1,12 +1,3 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.utility cimport pair -from libcpp.string cimport string -from libcpp cimport bool -import os - -import numpy as np - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -16,6 +7,15 @@ import numpy as np # Modification(s): # - YYYY/MM Author: Description of the modification +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.utility cimport pair +from libcpp.string cimport string +from libcpp cimport bool +import os + +import numpy as np + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/gudhi/euclidean_strong_witness_complex.pyx b/src/python/gudhi/euclidean_strong_witness_complex.pyx index e3f451f0..9889f92c 100644 --- a/src/python/gudhi/euclidean_strong_witness_complex.pyx +++ b/src/python/gudhi/euclidean_strong_witness_complex.pyx @@ -1,11 +1,3 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.utility cimport pair -from libc.stdint cimport intptr_t - -from gudhi.simplex_tree cimport * -from gudhi.simplex_tree import SimplexTree - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -15,6 +7,14 @@ from gudhi.simplex_tree import SimplexTree # Modification(s): # - YYYY/MM Author: Description of the modification +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.utility cimport pair +from libc.stdint cimport intptr_t + +from gudhi.simplex_tree cimport * +from gudhi.simplex_tree import SimplexTree + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "GPL v3" diff --git a/src/python/gudhi/euclidean_witness_complex.pyx b/src/python/gudhi/euclidean_witness_complex.pyx index 84a8ea1a..e3ce0e82 100644 --- a/src/python/gudhi/euclidean_witness_complex.pyx +++ b/src/python/gudhi/euclidean_witness_complex.pyx @@ -1,11 +1,3 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.utility cimport pair -from libc.stdint cimport intptr_t - -from gudhi.simplex_tree cimport * -from gudhi.simplex_tree import SimplexTree - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -15,6 +7,14 @@ from gudhi.simplex_tree import SimplexTree # Modification(s): # - YYYY/MM Author: Description of the modification +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.utility cimport pair +from libc.stdint cimport intptr_t + +from gudhi.simplex_tree cimport * +from gudhi.simplex_tree import SimplexTree + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "GPL v3" diff --git a/src/python/gudhi/nerve_gic.pyx b/src/python/gudhi/nerve_gic.pyx index acb78564..68c06432 100644 --- a/src/python/gudhi/nerve_gic.pyx +++ b/src/python/gudhi/nerve_gic.pyx @@ -1,3 +1,12 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2018 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + from cython cimport numeric from libcpp.vector cimport vector from libcpp.utility cimport pair @@ -9,15 +18,6 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Vincent Rouvreau -# -# Copyright (C) 2018 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2018 Inria" __license__ = "GPL v3" diff --git a/src/python/gudhi/off_reader.pyx b/src/python/gudhi/off_reader.pyx index 225e981c..54a275e2 100644 --- a/src/python/gudhi/off_reader.pyx +++ b/src/python/gudhi/off_reader.pyx @@ -1,8 +1,3 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.string cimport string -import os - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -12,6 +7,11 @@ import os # Modification(s): # - YYYY/MM Author: Description of the modification +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.string cimport string +import os + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index eb96ab5f..b5dece10 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -1,12 +1,3 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.utility cimport pair -from libcpp.string cimport string -from libcpp cimport bool -import os - -import numpy as np - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -16,6 +7,15 @@ import numpy as np # Modification(s): # - YYYY/MM Author: Description of the modification +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.utility cimport pair +from libcpp.string cimport string +from libcpp cimport bool +import os + +import numpy as np + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/gudhi/persistence_graphical_tools.py b/src/python/gudhi/persistence_graphical_tools.py index 7d232c85..246280de 100644 --- a/src/python/gudhi/persistence_graphical_tools.py +++ b/src/python/gudhi/persistence_graphical_tools.py @@ -1,10 +1,3 @@ -from os import path -from math import isfinite -import numpy as np - -from gudhi.reader_utils import read_persistence_intervals_in_dimension -from gudhi.reader_utils import read_persistence_intervals_grouped_by_dimension - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau, Bertrand Michel @@ -14,6 +7,13 @@ from gudhi.reader_utils import read_persistence_intervals_grouped_by_dimension # Modification(s): # - YYYY/MM Author: Description of the modification +from os import path +from math import isfinite +import numpy as np + +from gudhi.reader_utils import read_persistence_intervals_in_dimension +from gudhi.reader_utils import read_persistence_intervals_grouped_by_dimension + __author__ = "Vincent Rouvreau, Bertrand Michel" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/gudhi/reader_utils.pyx b/src/python/gudhi/reader_utils.pyx index 6994c4f9..345c92f8 100644 --- a/src/python/gudhi/reader_utils.pyx +++ b/src/python/gudhi/reader_utils.pyx @@ -1,12 +1,3 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.string cimport string -from libcpp.map cimport map -from libcpp.pair cimport pair - -from os import path -from numpy import array as np_array - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -16,6 +7,15 @@ from numpy import array as np_array # Modification(s): # - YYYY/MM Author: Description of the modification +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.string cimport string +from libcpp.map cimport map +from libcpp.pair cimport pair + +from os import path +from numpy import array as np_array + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2017 Inria" __license__ = "MIT" diff --git a/src/python/gudhi/rips_complex.pyx b/src/python/gudhi/rips_complex.pyx index cbbbab0d..722cdcdc 100644 --- a/src/python/gudhi/rips_complex.pyx +++ b/src/python/gudhi/rips_complex.pyx @@ -1,3 +1,12 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + from cython cimport numeric from libcpp.vector cimport vector from libcpp.utility cimport pair @@ -8,15 +17,6 @@ from libc.stdint cimport intptr_t from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Vincent Rouvreau -# -# Copyright (C) 2016 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/gudhi/simplex_tree.pxd b/src/python/gudhi/simplex_tree.pxd index 5f86cfe2..1066d44b 100644 --- a/src/python/gudhi/simplex_tree.pxd +++ b/src/python/gudhi/simplex_tree.pxd @@ -1,19 +1,18 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + from cython cimport numeric from libcpp.vector cimport vector from libcpp.utility cimport pair from libcpp cimport bool from libcpp.string cimport string -""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - Author(s): Vincent Rouvreau - - Copyright (C) 2016 Inria - - Modification(s): - - YYYY/MM Author: Description of the modification -""" - __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 4a3cd9bc..85d25492 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -1,7 +1,3 @@ -from libc.stdint cimport intptr_t -from numpy import array as np_array -cimport simplex_tree - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -11,6 +7,10 @@ cimport simplex_tree # Modification(s): # - YYYY/MM Author: Description of the modification +from libc.stdint cimport intptr_t +from numpy import array as np_array +cimport simplex_tree + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/gudhi/strong_witness_complex.pyx b/src/python/gudhi/strong_witness_complex.pyx index 66d49b49..2c33c3f2 100644 --- a/src/python/gudhi/strong_witness_complex.pyx +++ b/src/python/gudhi/strong_witness_complex.pyx @@ -1,11 +1,3 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.utility cimport pair -from libc.stdint cimport intptr_t - -from gudhi.simplex_tree cimport * -from gudhi.simplex_tree import SimplexTree - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -15,6 +7,14 @@ from gudhi.simplex_tree import SimplexTree # Modification(s): # - YYYY/MM Author: Description of the modification +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.utility cimport pair +from libc.stdint cimport intptr_t + +from gudhi.simplex_tree cimport * +from gudhi.simplex_tree import SimplexTree + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/gudhi/subsampling.pyx b/src/python/gudhi/subsampling.pyx index e0cd1348..b1812087 100644 --- a/src/python/gudhi/subsampling.pyx +++ b/src/python/gudhi/subsampling.pyx @@ -1,9 +1,3 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.string cimport string -from libcpp cimport bool -import os - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -13,6 +7,12 @@ import os # Modification(s): # - YYYY/MM Author: Description of the modification +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.string cimport string +from libcpp cimport bool +import os + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "GPL v3" diff --git a/src/python/gudhi/tangential_complex.pyx b/src/python/gudhi/tangential_complex.pyx index f4c8b079..0083033c 100644 --- a/src/python/gudhi/tangential_complex.pyx +++ b/src/python/gudhi/tangential_complex.pyx @@ -1,3 +1,12 @@ +# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. +# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. +# Author(s): Vincent Rouvreau +# +# Copyright (C) 2016 Inria +# +# Modification(s): +# - YYYY/MM Author: Description of the modification + from cython cimport numeric from libcpp.vector cimport vector from libcpp.utility cimport pair @@ -9,15 +18,6 @@ import os from gudhi.simplex_tree cimport * from gudhi.simplex_tree import SimplexTree -# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. -# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. -# Author(s): Vincent Rouvreau -# -# Copyright (C) 2016 Inria -# -# Modification(s): -# - YYYY/MM Author: Description of the modification - __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "GPL v3" diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index d8a3104c..1906eeb1 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -1,10 +1,3 @@ -import numpy as np -import scipy.spatial.distance as sc -try: - import ot -except ImportError: - print("POT (Python Optimal Transport) package is not installed. Try to run $ conda install -c conda-forge pot ; or $ pip install POT") - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Theo Lacombe @@ -14,6 +7,13 @@ except ImportError: # Modification(s): # - YYYY/MM Author: Description of the modification +import numpy as np +import scipy.spatial.distance as sc +try: + import ot +except ImportError: + print("POT (Python Optimal Transport) package is not installed. Try to run $ conda install -c conda-forge pot ; or $ pip install POT") + def _proj_on_diag(X): ''' :param X: (n x 2) array encoding the points of a persistent diagram. diff --git a/src/python/gudhi/witness_complex.pyx b/src/python/gudhi/witness_complex.pyx index 153fc615..b032a5a1 100644 --- a/src/python/gudhi/witness_complex.pyx +++ b/src/python/gudhi/witness_complex.pyx @@ -1,11 +1,3 @@ -from cython cimport numeric -from libcpp.vector cimport vector -from libcpp.utility cimport pair -from libc.stdint cimport intptr_t - -from gudhi.simplex_tree cimport * -from gudhi.simplex_tree import SimplexTree - # This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. # See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. # Author(s): Vincent Rouvreau @@ -15,6 +7,14 @@ from gudhi.simplex_tree import SimplexTree # Modification(s): # - YYYY/MM Author: Description of the modification +from cython cimport numeric +from libcpp.vector cimport vector +from libcpp.utility cimport pair +from libc.stdint cimport intptr_t + +from gudhi.simplex_tree cimport * +from gudhi.simplex_tree import SimplexTree + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/setup.py.in b/src/python/setup.py.in index 3f1d4424..24d05025 100644 --- a/src/python/setup.py.in +++ b/src/python/setup.py.in @@ -1,7 +1,3 @@ -from setuptools import setup, Extension -from Cython.Build import cythonize -from numpy import get_include as numpy_get_include - """This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -12,6 +8,10 @@ from numpy import get_include as numpy_get_include - YYYY/MM Author: Description of the modification """ +from setuptools import setup, Extension +from Cython.Build import cythonize +from numpy import get_include as numpy_get_include + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/test/test_alpha_complex.py b/src/python/test/test_alpha_complex.py index 9b27fff2..712a50b6 100755 --- a/src/python/test/test_alpha_complex.py +++ b/src/python/test/test_alpha_complex.py @@ -1,9 +1,3 @@ -from gudhi import AlphaComplex, SimplexTree -import math -import numpy as np -import itertools -import pytest - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -14,6 +8,12 @@ import pytest - YYYY/MM Author: Description of the modification """ +from gudhi import AlphaComplex, SimplexTree +import math +import numpy as np +import itertools +import pytest + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/test/test_bottleneck_distance.py b/src/python/test/test_bottleneck_distance.py index f5f019b9..70b2abad 100755 --- a/src/python/test/test_bottleneck_distance.py +++ b/src/python/test/test_bottleneck_distance.py @@ -1,5 +1,3 @@ -import gudhi - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -10,6 +8,8 @@ import gudhi - YYYY/MM Author: Description of the modification """ +import gudhi + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/test/test_cover_complex.py b/src/python/test/test_cover_complex.py index 8cd12272..32bc5a26 100755 --- a/src/python/test/test_cover_complex.py +++ b/src/python/test/test_cover_complex.py @@ -1,5 +1,3 @@ -from gudhi import CoverComplex - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -10,6 +8,8 @@ from gudhi import CoverComplex - YYYY/MM Author: Description of the modification """ +from gudhi import CoverComplex + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2018 Inria" __license__ = "MIT" diff --git a/src/python/test/test_cubical_complex.py b/src/python/test/test_cubical_complex.py index 121da12a..8c1b2600 100755 --- a/src/python/test/test_cubical_complex.py +++ b/src/python/test/test_cubical_complex.py @@ -1,6 +1,3 @@ -from gudhi import CubicalComplex, PeriodicCubicalComplex -import numpy as np - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -11,6 +8,9 @@ import numpy as np - YYYY/MM Author: Description of the modification """ +from gudhi import CubicalComplex, PeriodicCubicalComplex +import numpy as np + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/test/test_euclidean_witness_complex.py b/src/python/test/test_euclidean_witness_complex.py index f5eae5fa..c18d2484 100755 --- a/src/python/test/test_euclidean_witness_complex.py +++ b/src/python/test/test_euclidean_witness_complex.py @@ -1,5 +1,3 @@ -import gudhi - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -10,6 +8,8 @@ import gudhi - YYYY/MM Author: Description of the modification """ +import gudhi + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/test/test_reader_utils.py b/src/python/test/test_reader_utils.py index 4c7b32c2..90da6651 100755 --- a/src/python/test/test_reader_utils.py +++ b/src/python/test/test_reader_utils.py @@ -1,6 +1,3 @@ -import gudhi -import numpy as np - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -11,6 +8,9 @@ import numpy as np - YYYY/MM Author: Description of the modification """ +import gudhi +import numpy as np + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2017 Inria" __license__ = "MIT" diff --git a/src/python/test/test_representations.py b/src/python/test/test_representations.py index 4ff65f98..dba7f952 100755 --- a/src/python/test/test_representations.py +++ b/src/python/test/test_representations.py @@ -1,11 +1,12 @@ import os import sys import matplotlib.pyplot as plt -# Disable graphics for testing purposes -plt.show = lambda:None -here = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(here + "/../example") -import diagram_vectorizations_distances_kernels -# pytest is unhappy if there are 0 tests -def test_nothing(): + +def test_representations_examples(): + # Disable graphics for testing purposes + plt.show = lambda:None + here = os.path.dirname(os.path.realpath(__file__)) + sys.path.append(here + "/../example") + import diagram_vectorizations_distances_kernels + return None diff --git a/src/python/test/test_rips_complex.py b/src/python/test/test_rips_complex.py index d55ae22f..b02a68e1 100755 --- a/src/python/test/test_rips_complex.py +++ b/src/python/test/test_rips_complex.py @@ -1,6 +1,3 @@ -from gudhi import RipsComplex -from math import sqrt - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -11,6 +8,9 @@ from math import sqrt - YYYY/MM Author: Description of the modification """ +from gudhi import RipsComplex +from math import sqrt + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/test/test_simplex_tree.py b/src/python/test/test_simplex_tree.py index 8d8971c1..1822c43b 100755 --- a/src/python/test/test_simplex_tree.py +++ b/src/python/test/test_simplex_tree.py @@ -1,5 +1,3 @@ -from gudhi import SimplexTree - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -10,6 +8,8 @@ from gudhi import SimplexTree - YYYY/MM Author: Description of the modification """ +from gudhi import SimplexTree + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/test/test_subsampling.py b/src/python/test/test_subsampling.py index c816e203..fe0985fa 100755 --- a/src/python/test/test_subsampling.py +++ b/src/python/test/test_subsampling.py @@ -1,5 +1,3 @@ -import gudhi - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -10,6 +8,8 @@ import gudhi - YYYY/MM Author: Description of the modification """ +import gudhi + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/test/test_tangential_complex.py b/src/python/test/test_tangential_complex.py index 0f828d8e..e650e99c 100755 --- a/src/python/test/test_tangential_complex.py +++ b/src/python/test/test_tangential_complex.py @@ -1,5 +1,3 @@ -from gudhi import TangentialComplex, SimplexTree - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -10,6 +8,8 @@ from gudhi import TangentialComplex, SimplexTree - YYYY/MM Author: Description of the modification """ +from gudhi import TangentialComplex, SimplexTree + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" diff --git a/src/python/test/test_wasserstein_distance.py b/src/python/test/test_wasserstein_distance.py index a6bf9901..bb59b3c2 100755 --- a/src/python/test/test_wasserstein_distance.py +++ b/src/python/test/test_wasserstein_distance.py @@ -1,6 +1,3 @@ -from gudhi.wasserstein import wasserstein_distance -import numpy as np - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Theo Lacombe @@ -11,6 +8,9 @@ import numpy as np - YYYY/MM Author: Description of the modification """ +from gudhi.wasserstein import wasserstein_distance +import numpy as np + __author__ = "Theo Lacombe" __copyright__ = "Copyright (C) 2019 Inria" __license__ = "MIT" diff --git a/src/python/test/test_witness_complex.py b/src/python/test/test_witness_complex.py index 36ced635..7baf18c9 100755 --- a/src/python/test/test_witness_complex.py +++ b/src/python/test/test_witness_complex.py @@ -1,5 +1,3 @@ -from gudhi import WitnessComplex, StrongWitnessComplex, SimplexTree - """ This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. Author(s): Vincent Rouvreau @@ -10,6 +8,8 @@ from gudhi import WitnessComplex, StrongWitnessComplex, SimplexTree - YYYY/MM Author: Description of the modification """ +from gudhi import WitnessComplex, StrongWitnessComplex, SimplexTree + __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" -- cgit v1.2.3 From b6e1d3025e27eed08eb8a1c5f95a80d6c6a9ce15 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 13 Jan 2020 09:55:01 +0100 Subject: Fix #194 : Rename read_off as it only read points from OFF file --- src/python/doc/persistence_graphical_tools_user.rst | 2 +- src/python/doc/reader_utils_ref.rst | 2 +- src/python/doc/rips_complex_user.rst | 3 ++- src/python/doc/witness_complex_user.rst | 2 +- src/python/example/alpha_rips_persistence_bottleneck_distance.py | 2 +- ...strong_witness_complex_diagram_persistence_from_off_file_example.py | 2 +- ...lidean_witness_complex_diagram_persistence_from_off_file_example.py | 2 +- src/python/example/plot_rips_complex.py | 2 +- .../example/rips_complex_diagram_persistence_from_off_file_example.py | 2 +- src/python/gudhi/off_reader.pyx | 2 +- 10 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/python/doc/persistence_graphical_tools_user.rst b/src/python/doc/persistence_graphical_tools_user.rst index f41a926b..80002db6 100644 --- a/src/python/doc/persistence_graphical_tools_user.rst +++ b/src/python/doc/persistence_graphical_tools_user.rst @@ -24,7 +24,7 @@ This function can display the persistence result as a barcode: import gudhi off_file = gudhi.__root_source_dir__ + '/data/points/tore3D_300.off' - point_cloud = gudhi.read_off(off_file=off_file) + point_cloud = gudhi.read_points_from_off_file(off_file=off_file) rips_complex = gudhi.RipsComplex(points=point_cloud, max_edge_length=0.7) simplex_tree = rips_complex.create_simplex_tree(max_dimension=3) diff --git a/src/python/doc/reader_utils_ref.rst b/src/python/doc/reader_utils_ref.rst index f3ecebad..b8977a5a 100644 --- a/src/python/doc/reader_utils_ref.rst +++ b/src/python/doc/reader_utils_ref.rst @@ -6,7 +6,7 @@ Reader utils reference manual ============================= -.. autofunction:: gudhi.read_off +.. autofunction:: gudhi.read_points_from_off_file .. autofunction:: gudhi.read_lower_triangular_matrix_from_csv_file diff --git a/src/python/doc/rips_complex_user.rst b/src/python/doc/rips_complex_user.rst index a8659542..a27573e8 100644 --- a/src/python/doc/rips_complex_user.rst +++ b/src/python/doc/rips_complex_user.rst @@ -136,7 +136,8 @@ Finally, it is asked to display information about the Rips complex. .. testcode:: import gudhi - point_cloud = gudhi.read_off(off_file=gudhi.__root_source_dir__ + '/data/points/alphacomplexdoc.off') + off_file = gudhi.__root_source_dir__ + '/data/points/alphacomplexdoc.off' + point_cloud = gudhi.read_points_from_off_file(off_file = off_file) rips_complex = gudhi.RipsComplex(points=point_cloud, max_edge_length=12.0) simplex_tree = rips_complex.create_simplex_tree(max_dimension=1) result_str = 'Rips complex is of dimension ' + repr(simplex_tree.dimension()) + ' - ' + \ diff --git a/src/python/doc/witness_complex_user.rst b/src/python/doc/witness_complex_user.rst index 45ba5b3b..7087fa98 100644 --- a/src/python/doc/witness_complex_user.rst +++ b/src/python/doc/witness_complex_user.rst @@ -101,7 +101,7 @@ Let's start with a simple example, which reads an off point file and computes a print("#####################################################################") print("EuclideanWitnessComplex creation from points read in a OFF file") - witnesses = gudhi.read_off(off_file=args.file) + witnesses = gudhi.read_points_from_off_file(off_file=args.file) landmarks = gudhi.pick_n_random_points(points=witnesses, nb_points=args.number_of_landmarks) message = "EuclideanWitnessComplex with max_edge_length=" + repr(args.max_alpha_square) + \ diff --git a/src/python/example/alpha_rips_persistence_bottleneck_distance.py b/src/python/example/alpha_rips_persistence_bottleneck_distance.py index 086307ee..d5c33ec8 100755 --- a/src/python/example/alpha_rips_persistence_bottleneck_distance.py +++ b/src/python/example/alpha_rips_persistence_bottleneck_distance.py @@ -35,7 +35,7 @@ args = parser.parse_args() with open(args.file, "r") as f: first_line = f.readline() if (first_line == "OFF\n") or (first_line == "nOFF\n"): - point_cloud = gudhi.read_off(off_file=args.file) + point_cloud = gudhi.read_points_from_off_file(off_file=args.file) print("#####################################################################") print("RipsComplex creation from points read in a OFF file") diff --git a/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py b/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py index 0eedd140..4903667e 100755 --- a/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py @@ -47,7 +47,7 @@ with open(args.file, "r") as f: print("#####################################################################") print("EuclideanStrongWitnessComplex creation from points read in a OFF file") - witnesses = gudhi.read_off(off_file=args.file) + witnesses = gudhi.read_points_from_off_file(off_file=args.file) landmarks = gudhi.pick_n_random_points( points=witnesses, nb_points=args.number_of_landmarks ) diff --git a/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py b/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py index 1fe55737..339a8577 100755 --- a/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py @@ -46,7 +46,7 @@ with open(args.file, "r") as f: print("#####################################################################") print("EuclideanWitnessComplex creation from points read in a OFF file") - witnesses = gudhi.read_off(off_file=args.file) + witnesses = gudhi.read_points_from_off_file(off_file=args.file) landmarks = gudhi.pick_n_random_points( points=witnesses, nb_points=args.number_of_landmarks ) diff --git a/src/python/example/plot_rips_complex.py b/src/python/example/plot_rips_complex.py index 1c878db1..214a3c0a 100755 --- a/src/python/example/plot_rips_complex.py +++ b/src/python/example/plot_rips_complex.py @@ -2,7 +2,7 @@ import numpy as np import gudhi -points = np.array(gudhi.read_off('../../data/points/Kl.off')) +points = np.array(gudhi.read_points_from_off_file('../../data/points/Kl.off')) rc = gudhi.RipsComplex(points=points, max_edge_length=.2) st = rc.create_simplex_tree(max_dimension=2) # We are only going to plot the triangles diff --git a/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py b/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py index b9074cf9..c757aca7 100755 --- a/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py +++ b/src/python/example/rips_complex_diagram_persistence_from_off_file_example.py @@ -48,7 +48,7 @@ with open(args.file, "r") as f: message = "RipsComplex with max_edge_length=" + repr(args.max_edge_length) print(message) - point_cloud = gudhi.read_off(off_file=args.file) + point_cloud = gudhi.read_points_from_off_file(off_file=args.file) rips_complex = gudhi.RipsComplex( points=point_cloud, max_edge_length=args.max_edge_length ) diff --git a/src/python/gudhi/off_reader.pyx b/src/python/gudhi/off_reader.pyx index 225e981c..ee369976 100644 --- a/src/python/gudhi/off_reader.pyx +++ b/src/python/gudhi/off_reader.pyx @@ -19,7 +19,7 @@ __license__ = "MIT" cdef extern from "Off_reader_interface.h" namespace "Gudhi": vector[vector[double]] read_points_from_OFF_file(string off_file) -def read_off(off_file=''): +def read_points_from_off_file(off_file=''): """Read points from OFF file. :param off_file: An OFF file style name. -- cgit v1.2.3 From 79bf59738ff9ee18824ae874b60138a6b26fd336 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Mon, 13 Jan 2020 13:05:22 +0100 Subject: Add scikit-learn as a third party library as it is required by persistence representations --- src/python/doc/installation.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 50a697c7..40f3f44b 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -257,6 +257,13 @@ The :doc:`Wasserstein distance ` module requires `POT `_, a library that provides several solvers for optimization problems related to Optimal Transport. +Scikit-learn +============ + +The :doc:`persistence representations ` module require +`scikit-learn `_, a Python-based ecosystem of +open-source software for machine learning. + SciPy ===== -- cgit v1.2.3 From e807654f30363ec92c0d24b702bfd081ec8aae5a Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 13 Jan 2020 15:13:35 +0100 Subject: update variable names, going for order and internal_p --- src/python/doc/wasserstein_distance_user.rst | 2 +- src/python/gudhi/wasserstein.py | 34 ++++++++++++++-------------- src/python/test/test_wasserstein_distance.py | 34 ++++++++++++++-------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/python/doc/wasserstein_distance_user.rst b/src/python/doc/wasserstein_distance_user.rst index 8862a5ce..32999a0c 100644 --- a/src/python/doc/wasserstein_distance_user.rst +++ b/src/python/doc/wasserstein_distance_user.rst @@ -30,7 +30,7 @@ Note that persistence diagrams must be submitted as (n x 2) numpy arrays and mus diag1 = np.array([[2.7, 3.7],[9.6, 14.],[34.2, 34.974]]) diag2 = np.array([[2.8, 4.45],[9.5, 14.1]]) - message = "Wasserstein distance value = " + '%.2f' % gudhi.wasserstein.wasserstein_distance(diag1, diag2, internal_p=2., q=1.) + message = "Wasserstein distance value = " + '%.2f' % gudhi.wasserstein.wasserstein_distance(diag1, diag2, order=1., internal_p=2.) print(message) The output is: diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index 2acf93d6..aef54f64 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -23,12 +23,12 @@ def _proj_on_diag(X): return np.array([Z , Z]).T -def _build_dist_matrix(X, Y, q=2., internal_p=2.): +def _build_dist_matrix(X, Y, order=2., internal_p=2.): ''' :param X: (n x 2) numpy.array encoding the (points of the) first diagram. :param Y: (m x 2) numpy.array encoding the second diagram. :param internal_p: Ground metric (i.e. norm l_q). - :param q: exponent for the Wasserstein metric. + :param order: exponent for the Wasserstein metric. :returns: (n+1) x (m+1) np.array encoding the cost matrix C. For 1 <= i <= n, 1 <= j <= m, C[i,j] encodes the distance between X[i] and Y[j], while C[i, m+1] (resp. C[n+1, j]) encodes the distance (to the p) between X[i] (resp Y[j]) and its orthogonal proj onto the diagonal. note also that C[n+1, m+1] = 0 (it costs nothing to move from the diagonal to the diagonal). @@ -36,13 +36,13 @@ def _build_dist_matrix(X, Y, q=2., internal_p=2.): Xdiag = _proj_on_diag(X) Ydiag = _proj_on_diag(Y) if np.isinf(internal_p): - C = sc.cdist(X,Y, metric='chebyshev')**q - Cxd = np.linalg.norm(X - Xdiag, ord=internal_p, axis=1)**q - Cdy = np.linalg.norm(Y - Ydiag, ord=internal_p, axis=1)**q + C = sc.cdist(X,Y, metric='chebyshev')**order + Cxd = np.linalg.norm(X - Xdiag, ord=internal_p, axis=1)**order + Cdy = np.linalg.norm(Y - Ydiag, ord=internal_p, axis=1)**order else: - C = sc.cdist(X,Y, metric='minkowski', p=internal_p)**q - Cxd = np.linalg.norm(X - Xdiag, ord=internal_p, axis=1)**q - Cdy = np.linalg.norm(Y - Ydiag, ord=internal_p, axis=1)**q + C = sc.cdist(X,Y, metric='minkowski', p=internal_p)**order + Cxd = np.linalg.norm(X - Xdiag, ord=internal_p, axis=1)**order + Cdy = np.linalg.norm(Y - Ydiag, ord=internal_p, axis=1)**order Cf = np.hstack((C, Cxd[:,None])) Cdy = np.append(Cdy, 0) @@ -51,23 +51,23 @@ def _build_dist_matrix(X, Y, q=2., internal_p=2.): return Cf -def _perstot(X, q, internal_p): +def _perstot(X, order, internal_p): ''' :param X: (n x 2) numpy.array (points of a given diagram). :param internal_p: Ground metric on the (upper-half) plane (i.e. norm l_p in R^2); Default value is 2 (Euclidean norm). - :param q: exponent for Wasserstein; Default value is 2. + :param order: exponent for Wasserstein; Default value is 2. :returns: float, the total persistence of the diagram (that is, its distance to the empty diagram). ''' Xdiag = _proj_on_diag(X) - return (np.sum(np.linalg.norm(X - Xdiag, ord=internal_p, axis=1)**q))**(1./q) + return (np.sum(np.linalg.norm(X - Xdiag, ord=internal_p, axis=1)**order))**(1./order) -def wasserstein_distance(X, Y, q=2., internal_p=2.): +def wasserstein_distance(X, Y, order=2., internal_p=2.): ''' :param X: (n x 2) numpy.array encoding the (finite points of the) first diagram. Must not contain essential points (i.e. with infinite coordinate). :param Y: (m x 2) numpy.array encoding the second diagram. :param internal_p: Ground metric on the (upper-half) plane (i.e. norm l_p in R^2); Default value is 2 (euclidean norm). - :param q: exponent for Wasserstein; Default value is 2. + :param order: exponent for Wasserstein; Default value is 2. :returns: the q-Wasserstein distance (1 <= q < infinity) with respect to the internal_p-norm as ground metric. :rtype: float ''' @@ -79,11 +79,11 @@ def wasserstein_distance(X, Y, q=2., internal_p=2.): if Y.size == 0: return 0. else: - return _perstot(Y, q, internal_p) + return _perstot(Y, order, internal_p) elif Y.size == 0: - return _perstot(X, q, internal_p) + return _perstot(X, order, internal_p) - M = _build_dist_matrix(X, Y, q=q, internal_p=internal_p) + M = _build_dist_matrix(X, Y, order=order, internal_p=internal_p) a = np.full(n+1, 1. / (n + m) ) # weight vector of the input diagram. Uniform here. a[-1] = a[-1] * m # normalized so that we have a probability measure, required by POT b = np.full(m+1, 1. / (n + m) ) # weight vector of the input diagram. Uniform here. @@ -94,5 +94,5 @@ def wasserstein_distance(X, Y, q=2., internal_p=2.): # The default numItermax=100000 is not sufficient for some examples with 5000 points, what is a good value? ot_cost = (n+m) * ot.emd2(a, b, M, numItermax=2000000) - return ot_cost ** (1./q) + return ot_cost ** (1./order) diff --git a/src/python/test/test_wasserstein_distance.py b/src/python/test/test_wasserstein_distance.py index 40112c1b..602e4bf1 100755 --- a/src/python/test/test_wasserstein_distance.py +++ b/src/python/test/test_wasserstein_distance.py @@ -23,26 +23,26 @@ def test_basic_wasserstein(): diag4 = np.array([[0, 3], [4, 8]]) emptydiag = np.array([[]]) - assert wasserstein_distance(emptydiag, emptydiag, internal_p=2., q=1.) == 0. - assert wasserstein_distance(emptydiag, emptydiag, internal_p=np.inf, q=1.) == 0. - assert wasserstein_distance(emptydiag, emptydiag, internal_p=np.inf, q=2.) == 0. - assert wasserstein_distance(emptydiag, emptydiag, internal_p=2., q=2.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, internal_p=2., order=1.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, internal_p=np.inf, order=1.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, internal_p=np.inf, order=2.) == 0. + assert wasserstein_distance(emptydiag, emptydiag, internal_p=2., order=2.) == 0. - assert wasserstein_distance(diag3, emptydiag, internal_p=np.inf, q=1.) == 2. - assert wasserstein_distance(diag3, emptydiag, internal_p=1., q=1.) == 4. + assert wasserstein_distance(diag3, emptydiag, internal_p=np.inf, order=1.) == 2. + assert wasserstein_distance(diag3, emptydiag, internal_p=1., order=1.) == 4. - assert wasserstein_distance(diag4, emptydiag, internal_p=1., q=2.) == 5. # thank you Pythagorician triplets - assert wasserstein_distance(diag4, emptydiag, internal_p=np.inf, q=2.) == 2.5 - assert wasserstein_distance(diag4, emptydiag, internal_p=2., q=2.) == 3.5355339059327378 + assert wasserstein_distance(diag4, emptydiag, internal_p=1., order=2.) == 5. # thank you Pythagorician triplets + assert wasserstein_distance(diag4, emptydiag, internal_p=np.inf, order=2.) == 2.5 + assert wasserstein_distance(diag4, emptydiag, internal_p=2., order=2.) == 3.5355339059327378 - assert wasserstein_distance(diag1, diag2, internal_p=2., q=1.) == 1.4453593023967701 - assert wasserstein_distance(diag1, diag2, internal_p=2.35, q=1.74) == 0.9772734057168739 + assert wasserstein_distance(diag1, diag2, internal_p=2., order=1.) == 1.4453593023967701 + assert wasserstein_distance(diag1, diag2, internal_p=2.35, order=1.74) == 0.9772734057168739 - assert wasserstein_distance(diag1, emptydiag, internal_p=2.35, q=1.7863) == 3.141592214572228 + assert wasserstein_distance(diag1, emptydiag, internal_p=2.35, order=1.7863) == 3.141592214572228 - assert wasserstein_distance(diag3, diag4, internal_p=1., q=1.) == 3. - assert wasserstein_distance(diag3, diag4, internal_p=np.inf, q=1.) == 3. # no diag matching here - assert wasserstein_distance(diag3, diag4, internal_p=np.inf, q=2.) == np.sqrt(5) - assert wasserstein_distance(diag3, diag4, internal_p=1., q=2.) == np.sqrt(5) - assert wasserstein_distance(diag3, diag4, internal_p=4.5, q=2.) == np.sqrt(5) + assert wasserstein_distance(diag3, diag4, internal_p=1., order=1.) == 3. + assert wasserstein_distance(diag3, diag4, internal_p=np.inf, order=1.) == 3. # no diag matching here + assert wasserstein_distance(diag3, diag4, internal_p=np.inf, order=2.) == np.sqrt(5) + assert wasserstein_distance(diag3, diag4, internal_p=1., order=2.) == np.sqrt(5) + assert wasserstein_distance(diag3, diag4, internal_p=4.5, order=2.) == np.sqrt(5) -- cgit v1.2.3 From cabafa3852c2d325b83593d34f16dfbc2d9eaefb Mon Sep 17 00:00:00 2001 From: tlacombe Date: Mon, 13 Jan 2020 17:30:38 +0100 Subject: fix typo in doc wasserstein_distance with new variables naming conventions. --- src/python/gudhi/wasserstein.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index aef54f64..b6495c80 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -55,7 +55,7 @@ def _perstot(X, order, internal_p): ''' :param X: (n x 2) numpy.array (points of a given diagram). :param internal_p: Ground metric on the (upper-half) plane (i.e. norm l_p in R^2); Default value is 2 (Euclidean norm). - :param order: exponent for Wasserstein; Default value is 2. + :param order: exponent for Wasserstein. Default value is 2. :returns: float, the total persistence of the diagram (that is, its distance to the empty diagram). ''' Xdiag = _proj_on_diag(X) @@ -68,7 +68,7 @@ def wasserstein_distance(X, Y, order=2., internal_p=2.): :param Y: (m x 2) numpy.array encoding the second diagram. :param internal_p: Ground metric on the (upper-half) plane (i.e. norm l_p in R^2); Default value is 2 (euclidean norm). :param order: exponent for Wasserstein; Default value is 2. - :returns: the q-Wasserstein distance (1 <= q < infinity) with respect to the internal_p-norm as ground metric. + :returns: the Wasserstein distance of order q (1 <= q < infinity) between persistence diagrams with respect to the internal_p-norm as ground metric. :rtype: float ''' n = len(X) -- cgit v1.2.3 From 9bbbc84ab7208ccd69934445202538e34439b304 Mon Sep 17 00:00:00 2001 From: Théo Lacombe Date: Mon, 13 Jan 2020 17:39:02 +0100 Subject: Update src/python/gudhi/wasserstein.py Co-Authored-By: Marc Glisse --- src/python/gudhi/wasserstein.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/python/gudhi/wasserstein.py b/src/python/gudhi/wasserstein.py index b6495c80..3eb7faef 100644 --- a/src/python/gudhi/wasserstein.py +++ b/src/python/gudhi/wasserstein.py @@ -27,7 +27,7 @@ def _build_dist_matrix(X, Y, order=2., internal_p=2.): ''' :param X: (n x 2) numpy.array encoding the (points of the) first diagram. :param Y: (m x 2) numpy.array encoding the second diagram. - :param internal_p: Ground metric (i.e. norm l_q). + :param internal_p: Ground metric (i.e. norm l_p). :param order: exponent for the Wasserstein metric. :returns: (n+1) x (m+1) np.array encoding the cost matrix C. For 1 <= i <= n, 1 <= j <= m, C[i,j] encodes the distance between X[i] and Y[j], while C[i, m+1] (resp. C[n+1, j]) encodes the distance (to the p) between X[i] (resp Y[j]) and its orthogonal proj onto the diagonal. @@ -95,4 +95,3 @@ def wasserstein_distance(X, Y, order=2., internal_p=2.): ot_cost = (n+m) * ot.emd2(a, b, M, numItermax=2000000) return ot_cost ** (1./order) - -- cgit v1.2.3 From 8848099734d1825c59e2bae5612883808d080d85 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 14 Jan 2020 16:16:24 +0100 Subject: Unlink Python2 for Python3 to be available through brew installation --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d6c82e70..60d32ef8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,10 +36,13 @@ cache: - $HOME/.cache/pip - $HOME/Library/Caches/Homebrew +before_install: + - brew update && brew unlink python@2 && brew upgrade python + addons: homebrew: - update: true packages: + - python3 - cmake - graphviz - doxygen @@ -49,7 +52,6 @@ addons: - mpfr - tbb - cgal - - python3 before_cache: - rm -f $HOME/.cache/pip/log/debug.log -- cgit v1.2.3 From 95c34035645e788d98a3fa6b059ad48024596906 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 15 Jan 2020 10:20:22 +0100 Subject: In python2 itertools zip_longest is named izip_longest --- src/python/test/test_alpha_complex.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/python/test/test_alpha_complex.py b/src/python/test/test_alpha_complex.py index 712a50b6..0d9e9e45 100755 --- a/src/python/test/test_alpha_complex.py +++ b/src/python/test/test_alpha_complex.py @@ -11,8 +11,13 @@ from gudhi import AlphaComplex, SimplexTree import math import numpy as np -import itertools import pytest +try: + # python3 + from itertools import zip_longest +except ImportError: + # python2 + from itertools import izip_longest as zip_longest __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" @@ -114,6 +119,6 @@ def test_safe_alpha_persistence_comparison(): diag1 = simplex_tree1.persistence() diag2 = simplex_tree2.persistence() - for (first_p, second_p) in itertools.zip_longest(diag1, diag2): + for (first_p, second_p) in zip_longest(diag1, diag2): assert first_p[0] == pytest.approx(second_p[0]) assert first_p[1] == pytest.approx(second_p[1]) -- cgit v1.2.3 From e1255fe356f1ff97533b33caa6179737a64c4898 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 15 Jan 2020 10:22:21 +0100 Subject: Fix #96 : set language_level. It does not work when using compiler_directives of cythonize. --- src/python/setup.py.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/python/setup.py.in b/src/python/setup.py.in index 24d05025..9c2124f4 100644 --- a/src/python/setup.py.in +++ b/src/python/setup.py.in @@ -11,6 +11,7 @@ from setuptools import setup, Extension from Cython.Build import cythonize from numpy import get_include as numpy_get_include +import sys __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" @@ -38,7 +39,8 @@ for module in modules: libraries=libraries, library_dirs=library_dirs, include_dirs=include_dirs, - runtime_library_dirs=runtime_library_dirs,)) + runtime_library_dirs=runtime_library_dirs, + cython_directives = {'language_level': str(sys.version_info[0])},)) setup( name = 'gudhi', -- cgit v1.2.3 From d747facccbe835be1384e569cf3d6e4c335c0364 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Wed, 15 Jan 2020 10:23:58 +0100 Subject: In python2, does not work. is ok for Python 2 and 3 --- src/python/gudhi/alpha_complex.pyx | 2 +- src/python/gudhi/cubical_complex.pyx | 2 +- src/python/gudhi/nerve_gic.pyx | 12 ++++++------ src/python/gudhi/off_reader.pyx | 2 +- src/python/gudhi/periodic_cubical_complex.pyx | 2 +- src/python/gudhi/reader_utils.pyx | 8 ++++---- src/python/gudhi/simplex_tree.pyx | 2 +- src/python/gudhi/subsampling.pyx | 8 ++++---- src/python/gudhi/tangential_complex.pyx | 2 +- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index db11416c..f3ca3dd5 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -69,7 +69,7 @@ cdef class AlphaComplex: def __cinit__(self, points = None, off_file = ''): if off_file: if os.path.isfile(off_file): - self.thisptr = new Alpha_complex_interface(str.encode(off_file), True) + self.thisptr = new Alpha_complex_interface(off_file.encode('utf-8'), True) else: print("file " + off_file + " not found.") else: diff --git a/src/python/gudhi/cubical_complex.pyx b/src/python/gudhi/cubical_complex.pyx index 92ff6411..cbeda014 100644 --- a/src/python/gudhi/cubical_complex.pyx +++ b/src/python/gudhi/cubical_complex.pyx @@ -85,7 +85,7 @@ cdef class CubicalComplex: elif ((dimensions is None) and (top_dimensional_cells is None) and (perseus_file != '')): if os.path.isfile(perseus_file): - self.thisptr = new Bitmap_cubical_complex_base_interface(str.encode(perseus_file)) + self.thisptr = new Bitmap_cubical_complex_base_interface(perseus_file.encode('utf-8')) else: print("file " + perseus_file + " not found.") else: diff --git a/src/python/gudhi/nerve_gic.pyx b/src/python/gudhi/nerve_gic.pyx index 68c06432..382e71c5 100644 --- a/src/python/gudhi/nerve_gic.pyx +++ b/src/python/gudhi/nerve_gic.pyx @@ -180,7 +180,7 @@ cdef class CoverComplex: :returns: Read file status. """ if os.path.isfile(off_file): - return self.thisptr.read_point_cloud(str.encode(off_file)) + return self.thisptr.read_point_cloud(off_file.encode('utf-8')) else: print("file " + off_file + " not found.") return False @@ -212,7 +212,7 @@ cdef class CoverComplex: :type color_file_name: string """ if os.path.isfile(color_file_name): - self.thisptr.set_color_from_file(str.encode(color_file_name)) + self.thisptr.set_color_from_file(color_file_name.encode('utf-8')) else: print("file " + color_file_name + " not found.") @@ -233,7 +233,7 @@ cdef class CoverComplex: :type cover_file_name: string """ if os.path.isfile(cover_file_name): - self.thisptr.set_cover_from_file(str.encode(cover_file_name)) + self.thisptr.set_cover_from_file(cover_file_name.encode('utf-8')) else: print("file " + cover_file_name + " not found.") @@ -266,7 +266,7 @@ cdef class CoverComplex: :type func_file_name: string """ if os.path.isfile(func_file_name): - self.thisptr.set_function_from_file(str.encode(func_file_name)) + self.thisptr.set_function_from_file(func_file_name.encode('utf-8')) else: print("file " + func_file_name + " not found.") @@ -307,7 +307,7 @@ cdef class CoverComplex: :type graph_file_name: string """ if os.path.isfile(graph_file_name): - self.thisptr.set_graph_from_file(str.encode(graph_file_name)) + self.thisptr.set_graph_from_file(graph_file_name.encode('utf-8')) else: print("file " + graph_file_name + " not found.") @@ -368,7 +368,7 @@ cdef class CoverComplex: :param type: either "GIC" or "Nerve". :type type: string """ - self.thisptr.set_type(str.encode(type)) + self.thisptr.set_type(type.encode('utf-8')) def set_verbose(self, verbose): """Specifies whether the program should display information or not. diff --git a/src/python/gudhi/off_reader.pyx b/src/python/gudhi/off_reader.pyx index 58f05db8..a0d5bf25 100644 --- a/src/python/gudhi/off_reader.pyx +++ b/src/python/gudhi/off_reader.pyx @@ -30,7 +30,7 @@ def read_points_from_off_file(off_file=''): """ if off_file: if os.path.isfile(off_file): - return read_points_from_OFF_file(str.encode(off_file)) + return read_points_from_OFF_file(off_file.encode('utf-8')) else: print("file " + off_file + " not found.") return [] diff --git a/src/python/gudhi/periodic_cubical_complex.pyx b/src/python/gudhi/periodic_cubical_complex.pyx index b5dece10..37f76201 100644 --- a/src/python/gudhi/periodic_cubical_complex.pyx +++ b/src/python/gudhi/periodic_cubical_complex.pyx @@ -93,7 +93,7 @@ cdef class PeriodicCubicalComplex: elif ((dimensions is None) and (top_dimensional_cells is None) and (periodic_dimensions is None) and (perseus_file != '')): if os.path.isfile(perseus_file): - self.thisptr = new Periodic_cubical_complex_base_interface(str.encode(perseus_file)) + self.thisptr = new Periodic_cubical_complex_base_interface(perseus_file.encode('utf-8')) else: print("file " + perseus_file + " not found.") else: diff --git a/src/python/gudhi/reader_utils.pyx b/src/python/gudhi/reader_utils.pyx index 345c92f8..d6033b86 100644 --- a/src/python/gudhi/reader_utils.pyx +++ b/src/python/gudhi/reader_utils.pyx @@ -38,7 +38,7 @@ def read_lower_triangular_matrix_from_csv_file(csv_file='', separator=';'): """ if csv_file: if path.isfile(csv_file): - return read_matrix_from_csv_file(str.encode(csv_file), ord(separator[0])) + return read_matrix_from_csv_file(csv_file.encode('utf-8'), ord(separator[0])) print("file " + csv_file + " not set or not found.") return [] @@ -57,7 +57,7 @@ def read_persistence_intervals_grouped_by_dimension(persistence_file=''): """ if persistence_file: if path.isfile(persistence_file): - return read_pers_intervals_grouped_by_dimension(str.encode(persistence_file)) + return read_pers_intervals_grouped_by_dimension(persistence_file.encode('utf-8')) print("file " + persistence_file + " not set or not found.") return [] @@ -80,7 +80,7 @@ def read_persistence_intervals_in_dimension(persistence_file='', only_this_dim=- """ if persistence_file: if path.isfile(persistence_file): - return np_array(read_pers_intervals_in_dimension(str.encode( - persistence_file), only_this_dim)) + return np_array(read_pers_intervals_in_dimension(persistence_file.encode( + 'utf-8'), only_this_dim)) print("file " + persistence_file + " not set or not found.") return [] diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 85d25492..b18627c4 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -508,7 +508,7 @@ cdef class SimplexTree: """ if self.pcohptr != NULL: if persistence_file != '': - self.pcohptr.write_output_diagram(str.encode(persistence_file)) + self.pcohptr.write_output_diagram(persistence_file.encode('utf-8')) else: print("persistence_file must be specified") else: diff --git a/src/python/gudhi/subsampling.pyx b/src/python/gudhi/subsampling.pyx index b1812087..c501d16b 100644 --- a/src/python/gudhi/subsampling.pyx +++ b/src/python/gudhi/subsampling.pyx @@ -52,10 +52,10 @@ def choose_n_farthest_points(points=None, off_file='', nb_points=0, starting_poi if off_file: if os.path.isfile(off_file): if starting_point == '': - return subsampling_n_farthest_points_from_file(str.encode(off_file), + return subsampling_n_farthest_points_from_file(off_file.encode('utf-8'), nb_points) else: - return subsampling_n_farthest_points_from_file(str.encode(off_file), + return subsampling_n_farthest_points_from_file(off_file.encode('utf-8'), nb_points, starting_point) else: @@ -88,7 +88,7 @@ def pick_n_random_points(points=None, off_file='', nb_points=0): """ if off_file: if os.path.isfile(off_file): - return subsampling_n_random_points_from_file(str.encode(off_file), + return subsampling_n_random_points_from_file(off_file.encode('utf-8'), nb_points) else: print("file " + off_file + " not found.") @@ -118,7 +118,7 @@ def sparsify_point_set(points=None, off_file='', min_squared_dist=0.0): """ if off_file: if os.path.isfile(off_file): - return subsampling_sparsify_points_from_file(str.encode(off_file), + return subsampling_sparsify_points_from_file(off_file.encode('utf-8'), min_squared_dist) else: print("file " + off_file + " not found.") diff --git a/src/python/gudhi/tangential_complex.pyx b/src/python/gudhi/tangential_complex.pyx index 0083033c..6391488c 100644 --- a/src/python/gudhi/tangential_complex.pyx +++ b/src/python/gudhi/tangential_complex.pyx @@ -66,7 +66,7 @@ cdef class TangentialComplex: def __cinit__(self, intrisic_dim, points=None, off_file=''): if off_file: if os.path.isfile(off_file): - self.thisptr = new Tangential_complex_interface(intrisic_dim, str.encode(off_file), True) + self.thisptr = new Tangential_complex_interface(intrisic_dim, off_file.encode('utf-8'), True) else: print("file " + off_file + " not found.") else: -- cgit v1.2.3 From 9fbd7f8fd558a6045ff7f6508bb668ff8e689eba Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 16 Jan 2020 14:53:49 +0100 Subject: Version 3.1.0 to be released --- CMakeGUDHIVersion.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeGUDHIVersion.txt b/CMakeGUDHIVersion.txt index eb2a0666..ed19ecfb 100644 --- a/CMakeGUDHIVersion.txt +++ b/CMakeGUDHIVersion.txt @@ -1,5 +1,5 @@ set (GUDHI_MAJOR_VERSION 3) -set (GUDHI_MINOR_VERSION 0) +set (GUDHI_MINOR_VERSION 1) set (GUDHI_PATCH_VERSION 0) set(GUDHI_VERSION ${GUDHI_MAJOR_VERSION}.${GUDHI_MINOR_VERSION}.${GUDHI_PATCH_VERSION}) -- cgit v1.2.3 From cabc43b34723efa7640313348b844eabe9971e38 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 16 Jan 2020 14:54:17 +0100 Subject: Version 3.1.0.rc1 to be released --- CMakeGUDHIVersion.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeGUDHIVersion.txt b/CMakeGUDHIVersion.txt index ed19ecfb..8300b75e 100644 --- a/CMakeGUDHIVersion.txt +++ b/CMakeGUDHIVersion.txt @@ -1,6 +1,6 @@ set (GUDHI_MAJOR_VERSION 3) set (GUDHI_MINOR_VERSION 1) -set (GUDHI_PATCH_VERSION 0) +set (GUDHI_PATCH_VERSION 0.rc1) set(GUDHI_VERSION ${GUDHI_MAJOR_VERSION}.${GUDHI_MINOR_VERSION}.${GUDHI_PATCH_VERSION}) message(STATUS "GUDHI version : ${GUDHI_VERSION}") -- cgit v1.2.3